@mosvera/mcp 0.1.1

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.
Files changed (75) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +165 -0
  3. package/dist/context.d.ts +18 -0
  4. package/dist/context.js +179 -0
  5. package/dist/errors.d.ts +8 -0
  6. package/dist/errors.js +19 -0
  7. package/dist/examples/README.md +16 -0
  8. package/dist/examples/cinematic-editorial/README.md +22 -0
  9. package/dist/examples/cinematic-editorial/composition.json +14 -0
  10. package/dist/examples/cinematic-editorial/manifests/illustrative-image.manifest.json +16 -0
  11. package/dist/examples/cinematic-editorial/merge-strategies.json +3 -0
  12. package/dist/examples/cinematic-editorial/modifier.golden-hour.json +10 -0
  13. package/dist/examples/cinematic-editorial/modifier.high-contrast.json +5 -0
  14. package/dist/examples/cinematic-editorial/palette.editorial-warm.json +10 -0
  15. package/dist/examples/cinematic-editorial/template.base.json +11 -0
  16. package/dist/examples/cinematic-editorial/template.noir.json +7 -0
  17. package/dist/examples/demo-aesthetics/README.md +11 -0
  18. package/dist/examples/demo-aesthetics/composition.cinematic-lab.json +5 -0
  19. package/dist/examples/demo-aesthetics/composition.claymation-playful-builder.json +5 -0
  20. package/dist/examples/demo-aesthetics/composition.quiet-editorial.json +5 -0
  21. package/dist/examples/demo-aesthetics/composition.technical-manual.json +5 -0
  22. package/dist/examples/demo-aesthetics/template.cinematic-lab-base.json +45 -0
  23. package/dist/examples/demo-aesthetics/template.claymation-playful-builder-base.json +45 -0
  24. package/dist/examples/demo-aesthetics/template.quiet-editorial-base.json +45 -0
  25. package/dist/examples/demo-aesthetics/template.technical-manual-base.json +45 -0
  26. package/dist/index.d.ts +4 -0
  27. package/dist/index.js +7 -0
  28. package/dist/mcp-result.d.ts +10 -0
  29. package/dist/mcp-result.js +26 -0
  30. package/dist/project-writes.d.ts +8 -0
  31. package/dist/project-writes.js +70 -0
  32. package/dist/registry/loader.d.ts +4 -0
  33. package/dist/registry/loader.js +85 -0
  34. package/dist/registry/preflight.d.ts +3 -0
  35. package/dist/registry/preflight.js +28 -0
  36. package/dist/registry/strategies.d.ts +9 -0
  37. package/dist/registry/strategies.js +30 -0
  38. package/dist/server.d.ts +6 -0
  39. package/dist/server.js +172 -0
  40. package/dist/tools/aesthetic.d.ts +57 -0
  41. package/dist/tools/aesthetic.js +331 -0
  42. package/dist/tools/compile-generation.d.ts +12 -0
  43. package/dist/tools/compile-generation.js +67 -0
  44. package/dist/tools/get-palette.d.ts +11 -0
  45. package/dist/tools/get-palette.js +23 -0
  46. package/dist/tools/list-templates.d.ts +6 -0
  47. package/dist/tools/list-templates.js +15 -0
  48. package/dist/tools/resolve-composition.d.ts +8 -0
  49. package/dist/tools/resolve-composition.js +38 -0
  50. package/dist/tools/validate-schema.d.ts +6 -0
  51. package/dist/tools/validate-schema.js +17 -0
  52. package/dist/types.d.ts +58 -0
  53. package/dist/types.js +7 -0
  54. package/examples/README.md +16 -0
  55. package/examples/cinematic-editorial/README.md +22 -0
  56. package/examples/cinematic-editorial/composition.json +14 -0
  57. package/examples/cinematic-editorial/manifests/illustrative-image.manifest.json +16 -0
  58. package/examples/cinematic-editorial/merge-strategies.json +3 -0
  59. package/examples/cinematic-editorial/modifier.golden-hour.json +10 -0
  60. package/examples/cinematic-editorial/modifier.high-contrast.json +5 -0
  61. package/examples/cinematic-editorial/palette.editorial-warm.json +10 -0
  62. package/examples/cinematic-editorial/template.base.json +11 -0
  63. package/examples/cinematic-editorial/template.noir.json +7 -0
  64. package/examples/demo-aesthetics/README.md +11 -0
  65. package/examples/demo-aesthetics/composition.cinematic-lab.json +5 -0
  66. package/examples/demo-aesthetics/composition.claymation-playful-builder.json +5 -0
  67. package/examples/demo-aesthetics/composition.quiet-editorial.json +5 -0
  68. package/examples/demo-aesthetics/composition.technical-manual.json +5 -0
  69. package/examples/demo-aesthetics/template.cinematic-lab-base.json +45 -0
  70. package/examples/demo-aesthetics/template.claymation-playful-builder-base.json +45 -0
  71. package/examples/demo-aesthetics/template.quiet-editorial-base.json +45 -0
  72. package/examples/demo-aesthetics/template.technical-manual-base.json +45 -0
  73. package/mcpb/icon.png +0 -0
  74. package/mcpb/manifest.json +73 -0
  75. package/package.json +74 -0
package/dist/server.js ADDED
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ //
4
+ // Mosvera MCP stdio bootstrap. The server exposes a small, user-facing
5
+ // aesthetic tool surface over the TypeScript runtime and a local project
6
+ // registry.
7
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ import { z } from "zod";
10
+ import { buildContext, parseCliOptions, SERVER_VERSION } from "./context.js";
11
+ import { documentKinds, runCompileDesignTokens, runCompileProviderPayload, runDeleteRegistryDocument, runDraftAesthetic, runGetRegistryDocument, runListAesthetics, runResolveAesthetic, runSaveAesthetic, runSaveRegistryDocument, runServerStatus, runValidateDocument, runValidateRegistry, runWriteMergeStrategies, } from "./tools/aesthetic.js";
12
+ const docArg = z.union([z.string(), z.record(z.any())]);
13
+ const strategiesArg = z.record(z.object({
14
+ strategy: z.enum(["replace", "append", "merge_by"]),
15
+ key: z.string().optional(),
16
+ }));
17
+ const criticalityArg = z.record(z.enum(["required", "optional"]));
18
+ const documentKindArg = z.enum(documentKinds);
19
+ const registryDocumentKindArg = z.enum(documentKinds);
20
+ const outputSchema = z.object({ ok: z.boolean(), message: z.string() }).passthrough();
21
+ const readAnnotations = {
22
+ readOnlyHint: true,
23
+ destructiveHint: false,
24
+ openWorldHint: false,
25
+ };
26
+ const writeAnnotations = {
27
+ readOnlyHint: false,
28
+ destructiveHint: false,
29
+ idempotentHint: true,
30
+ openWorldHint: false,
31
+ };
32
+ const destructiveAnnotations = {
33
+ readOnlyHint: false,
34
+ destructiveHint: true,
35
+ idempotentHint: true,
36
+ openWorldHint: false,
37
+ };
38
+ export { buildContext };
39
+ export function createServer(ctx) {
40
+ const server = new McpServer({
41
+ name: "mosvera-mcp",
42
+ title: "Mosvera MCP",
43
+ version: SERVER_VERSION,
44
+ });
45
+ server.registerTool("server_status", {
46
+ title: "Server Status",
47
+ description: "Show the active Mosvera registry path, write mode, versions, document counts, and diagnostics.",
48
+ inputSchema: {},
49
+ outputSchema,
50
+ annotations: readAnnotations,
51
+ }, async () => runServerStatus(ctx));
52
+ server.registerTool("list_aesthetics", {
53
+ title: "List Aesthetics",
54
+ description: "List named aesthetics available in the active local Mosvera registry.",
55
+ inputSchema: {},
56
+ outputSchema,
57
+ annotations: readAnnotations,
58
+ }, async () => runListAesthetics(ctx));
59
+ server.registerTool("get_registry_document", {
60
+ title: "Get Registry Document",
61
+ description: "Fetch a template, modifier, palette, composition, or capability manifest from the active registry.",
62
+ inputSchema: { kind: registryDocumentKindArg, id: z.string() },
63
+ outputSchema,
64
+ annotations: readAnnotations,
65
+ }, async (args) => runGetRegistryDocument(ctx, args));
66
+ server.registerTool("validate_document", {
67
+ title: "Validate Document",
68
+ description: "Validate one Mosvera document against a schema kind. Invalid documents return valid=false, not a thrown protocol error.",
69
+ inputSchema: { document: docArg, kind: documentKindArg },
70
+ outputSchema,
71
+ annotations: readAnnotations,
72
+ }, async (args) => runValidateDocument(ctx, args));
73
+ server.registerTool("validate_registry", {
74
+ title: "Validate Registry",
75
+ description: "Validate the active local registry and return Mosvera diagnostics.",
76
+ inputSchema: {},
77
+ outputSchema,
78
+ annotations: readAnnotations,
79
+ }, async () => runValidateRegistry(ctx));
80
+ server.registerTool("resolve_aesthetic", {
81
+ title: "Resolve Aesthetic",
82
+ description: "Resolve a named or inline aesthetic composition into the canonical Mosvera model.",
83
+ inputSchema: { aesthetic: z.union([z.string(), z.record(z.any())]), merge_strategies: strategiesArg.optional() },
84
+ outputSchema,
85
+ annotations: readAnnotations,
86
+ }, async (args) => runResolveAesthetic(ctx, args));
87
+ server.registerTool("compile_design_tokens", {
88
+ title: "Compile Design Tokens",
89
+ description: "Compile a named/inline aesthetic or canonical model into portable design tokens and CSS variables.",
90
+ inputSchema: {
91
+ aesthetic: z.union([z.string(), z.record(z.any())]).optional(),
92
+ canonical: docArg.optional(),
93
+ merge_strategies: strategiesArg.optional(),
94
+ css_prefix: z.string().optional(),
95
+ preserve_unknown: z.boolean().optional(),
96
+ },
97
+ outputSchema,
98
+ annotations: readAnnotations,
99
+ }, async (args) => runCompileDesignTokens(ctx, args));
100
+ server.registerTool("compile_provider_payload", {
101
+ title: "Compile Provider Payload",
102
+ description: "Advanced: deterministically compile a named/inline aesthetic into a provider payload. No provider HTTP call is made.",
103
+ inputSchema: {
104
+ aesthetic: z.union([z.string(), z.record(z.any())]),
105
+ provider: z.string(),
106
+ criticality: criticalityArg.optional(),
107
+ merge_strategies: strategiesArg.optional(),
108
+ },
109
+ outputSchema,
110
+ annotations: readAnnotations,
111
+ }, async (args) => runCompileProviderPayload(ctx, args));
112
+ server.registerTool("draft_aesthetic", {
113
+ title: "Draft Aesthetic",
114
+ description: "Draft a valid composition document without saving it to disk.",
115
+ inputSchema: {
116
+ id: z.string(),
117
+ base: z.string(),
118
+ modifiers: z.array(z.string()).optional(),
119
+ overrides: docArg.optional(),
120
+ },
121
+ outputSchema,
122
+ annotations: readAnnotations,
123
+ }, async (args) => runDraftAesthetic(ctx, args));
124
+ if (ctx.registryWritable && !ctx.readOnlyMode) {
125
+ server.registerTool("save_aesthetic", {
126
+ title: "Save Aesthetic",
127
+ description: "Create or update a named aesthetic composition in the active local registry.",
128
+ inputSchema: {
129
+ id: z.string(),
130
+ base: z.string(),
131
+ modifiers: z.array(z.string()).optional(),
132
+ overrides: docArg.optional(),
133
+ },
134
+ outputSchema,
135
+ annotations: writeAnnotations,
136
+ }, async (args) => runSaveAesthetic(ctx, args));
137
+ server.registerTool("save_registry_document", {
138
+ title: "Save Registry Document",
139
+ description: "Advanced: create or update a template, modifier, palette, composition, or capability manifest in the active local registry.",
140
+ inputSchema: { kind: registryDocumentKindArg, document: docArg },
141
+ outputSchema,
142
+ annotations: writeAnnotations,
143
+ }, async (args) => runSaveRegistryDocument(ctx, args));
144
+ server.registerTool("delete_registry_document", {
145
+ title: "Delete Registry Document",
146
+ description: "Delete a registry document from the active local registry.",
147
+ inputSchema: { kind: registryDocumentKindArg, id: z.string() },
148
+ outputSchema,
149
+ annotations: destructiveAnnotations,
150
+ }, async (args) => runDeleteRegistryDocument(ctx, args));
151
+ server.registerTool("write_merge_strategies", {
152
+ title: "Write Merge Strategies",
153
+ description: "Advanced: replace the active registry's merge-strategies.json with deterministic JSON.",
154
+ inputSchema: { merge_strategies: strategiesArg },
155
+ outputSchema,
156
+ annotations: writeAnnotations,
157
+ }, async (args) => runWriteMergeStrategies(ctx, args));
158
+ }
159
+ return server;
160
+ }
161
+ async function main() {
162
+ const options = parseCliOptions(process.argv.slice(2));
163
+ const ctx = buildContext(options);
164
+ const server = createServer(ctx);
165
+ await server.connect(new StdioServerTransport());
166
+ }
167
+ if (import.meta.url === `file://${process.argv[1]}`) {
168
+ main().catch((e) => {
169
+ process.stderr.write(`mosvera-mcp failed to start: ${e instanceof Error ? e.stack : String(e)}\n`);
170
+ process.exit(1);
171
+ });
172
+ }
@@ -0,0 +1,57 @@
1
+ import { type Criticality, type DocumentKind, type MergeStrategies, type RegistryKind } from "@mosvera/runtime";
2
+ import type { ToolContext, ToolErrorCode } from "../types.ts";
3
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4
+ export declare const registryKinds: readonly ["template", "modifier", "palette", "composition"];
5
+ export declare const documentKinds: readonly ["template", "modifier", "palette", "composition", "capability-manifest"];
6
+ export declare function runServerStatus(ctx: ToolContext): CallToolResult;
7
+ export declare function runListAesthetics(ctx: ToolContext): CallToolResult;
8
+ export declare function runGetRegistryDocument(ctx: ToolContext, args: {
9
+ kind: RegistryKind | "capability-manifest";
10
+ id: string;
11
+ }): CallToolResult;
12
+ export declare function runValidateDocument(ctx: ToolContext, args: {
13
+ document: object | string;
14
+ kind: DocumentKind;
15
+ }): CallToolResult;
16
+ export declare function runValidateRegistry(ctx: ToolContext): CallToolResult;
17
+ export declare function runResolveAesthetic(ctx: ToolContext, args: {
18
+ aesthetic: string | object;
19
+ merge_strategies?: MergeStrategies;
20
+ }): CallToolResult;
21
+ export declare function runCompileDesignTokens(ctx: ToolContext, args: {
22
+ aesthetic?: string | object;
23
+ canonical?: object | string;
24
+ merge_strategies?: MergeStrategies;
25
+ css_prefix?: string;
26
+ preserve_unknown?: boolean;
27
+ }): CallToolResult;
28
+ export declare function runCompileProviderPayload(ctx: ToolContext, args: {
29
+ aesthetic: string | object;
30
+ provider: string;
31
+ criticality?: Record<string, Criticality>;
32
+ merge_strategies?: MergeStrategies;
33
+ }): CallToolResult;
34
+ export declare function runDraftAesthetic(_ctx: ToolContext, args: {
35
+ id: string;
36
+ base: string;
37
+ modifiers?: string[];
38
+ overrides?: object | string;
39
+ }): CallToolResult;
40
+ export declare function runSaveAesthetic(ctx: ToolContext, args: {
41
+ id: string;
42
+ base: string;
43
+ modifiers?: string[];
44
+ overrides?: object | string;
45
+ }): CallToolResult;
46
+ export declare function runSaveRegistryDocument(ctx: ToolContext, args: {
47
+ kind: RegistryKind | "capability-manifest";
48
+ document: object | string;
49
+ }): CallToolResult;
50
+ export declare function runDeleteRegistryDocument(ctx: ToolContext, args: {
51
+ kind: RegistryKind | "capability-manifest";
52
+ id: string;
53
+ }): CallToolResult;
54
+ export declare function runWriteMergeStrategies(ctx: ToolContext, args: {
55
+ merge_strategies: MergeStrategies;
56
+ }): CallToolResult;
57
+ export declare function errorCodeFromResult(result: CallToolResult): ToolErrorCode | undefined;
@@ -0,0 +1,331 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ //
3
+ // User-facing Mosvera MCP tools. These handlers keep the MCP layer thin:
4
+ // parse/validate inputs, call @mosvera/runtime, persist through the Node
5
+ // boundary where needed, and return MCP-native structured results.
6
+ import { compile, compileDesignTokens, composeStrategies, createComposition, getRegistryDocument, listRegistryEntries, parse, resolveAesthetic, toCssVariables, } from "@mosvera/runtime";
7
+ import { deleteProjectDocument, RegistryProjectError, saveProjectDocument, writeMergeStrategies } from "@mosvera/runtime/node";
8
+ import { EmissionError } from "@mosvera/provider-base";
9
+ import { registryDiagnostics, reloadContext, SERVER_VERSION } from "../context.js";
10
+ import { fail, ok } from "../mcp-result.js";
11
+ import { deleteCapabilityManifest, ProjectWriteError, saveCapabilityManifest } from "../project-writes.js";
12
+ import { isResolutionError, mapResolutionError } from "../errors.js";
13
+ export const registryKinds = ["template", "modifier", "palette", "composition"];
14
+ export const documentKinds = ["template", "modifier", "palette", "composition", "capability-manifest"];
15
+ function parseDocument(source) {
16
+ try {
17
+ return parse(source);
18
+ }
19
+ catch (e) {
20
+ return {
21
+ error: "invalid_document",
22
+ message: `Could not parse Mosvera document: ${e instanceof Error ? e.message : String(e)}`,
23
+ };
24
+ }
25
+ }
26
+ function runtimeError(e) {
27
+ if (isResolutionError(e)) {
28
+ const mapped = mapResolutionError(e);
29
+ return { error: mapped.error, message: `Could not resolve aesthetic: ${mapped.error}`, detail: mapped.detail };
30
+ }
31
+ if (e instanceof ProjectWriteError) {
32
+ return { error: e.code, message: e.message, detail: e.detail };
33
+ }
34
+ if (e instanceof RegistryProjectError) {
35
+ const first = e.diagnostics[0];
36
+ return {
37
+ error: first?.code ?? "invalid_document",
38
+ message: first?.message ?? e.message,
39
+ detail: { diagnostics: e.diagnostics },
40
+ };
41
+ }
42
+ if (e instanceof EmissionError) {
43
+ return { error: "invalid_document", message: e.message, detail: { construct: e.construct } };
44
+ }
45
+ if (e instanceof Error && e.message.includes("invalid Mosvera reference id")) {
46
+ return { error: "unsafe_filename", message: e.message };
47
+ }
48
+ return {
49
+ error: "invalid_document",
50
+ message: e instanceof Error ? e.message : String(e),
51
+ };
52
+ }
53
+ function maybeFail(value) {
54
+ return typeof value === "object" && value !== null && "error" in value && "message" in value;
55
+ }
56
+ function strategies(ctx, inline) {
57
+ return composeStrategies(ctx.baseStrategies, inline);
58
+ }
59
+ function resolveInput(ctx, args) {
60
+ if (args.canonical !== undefined) {
61
+ const canonical = parseDocument(args.canonical);
62
+ if (maybeFail(canonical))
63
+ return canonical;
64
+ return { canonical, source: "canonical" };
65
+ }
66
+ const aesthetic = args.aesthetic ?? "quiet-editorial";
67
+ const docOrId = typeof aesthetic === "string" ? aesthetic : parseDocument(aesthetic);
68
+ if (maybeFail(docOrId))
69
+ return docOrId;
70
+ try {
71
+ return {
72
+ canonical: resolveAesthetic(docOrId, ctx.project.registry, strategies(ctx, args.merge_strategies)),
73
+ source: "aesthetic",
74
+ };
75
+ }
76
+ catch (e) {
77
+ return runtimeError(e);
78
+ }
79
+ }
80
+ function validateForSave(ctx, document, kind) {
81
+ const result = ctx.validator.validate(document, kind);
82
+ if (result.valid)
83
+ return undefined;
84
+ return {
85
+ error: "schema_failure",
86
+ message: `${kind} document failed schema validation`,
87
+ detail: { errors: result.errors },
88
+ };
89
+ }
90
+ function writeDisabled(ctx) {
91
+ if (ctx.readOnlyMode)
92
+ return { error: "write_disabled", message: "This Mosvera MCP server is running in read-only mode." };
93
+ if (!ctx.registryWritable)
94
+ return { error: "registry_unwritable", message: "The active Mosvera registry is not writable." };
95
+ return undefined;
96
+ }
97
+ function toolFail(failure) {
98
+ return fail(failure.error, failure.message, failure.detail);
99
+ }
100
+ export function runServerStatus(ctx) {
101
+ const diagnostics = [...ctx.loadDiagnostics, ...registryDiagnostics(ctx.project, ctx.validator)];
102
+ const registry = ctx.project.registry;
103
+ const counts = {
104
+ templates: Object.keys(registry.templates ?? {}).length,
105
+ modifiers: Object.keys(registry.modifiers ?? {}).length,
106
+ palettes: Object.keys(registry.palettes ?? {}).length,
107
+ compositions: Object.keys(registry.compositions ?? {}).length,
108
+ manifests: Object.keys(ctx.project.manifests).length,
109
+ };
110
+ return ok("Mosvera MCP server is ready.", {
111
+ registry_path: ctx.registryDir,
112
+ registry_writable: ctx.registryWritable,
113
+ read_only_mode: ctx.readOnlyMode,
114
+ write_tools_enabled: ctx.registryWritable && !ctx.readOnlyMode,
115
+ fallback_registry: ctx.fallbackRegistry,
116
+ fallback_reason: ctx.fallbackReason,
117
+ versions: {
118
+ mcp: SERVER_VERSION,
119
+ runtime: "0.1.1",
120
+ },
121
+ counts,
122
+ diagnostics,
123
+ });
124
+ }
125
+ export function runListAesthetics(ctx) {
126
+ const aesthetics = listRegistryEntries(ctx.project.registry, "composition");
127
+ return ok(`Found ${aesthetics.length} aesthetic${aesthetics.length === 1 ? "" : "s"}.`, { aesthetics });
128
+ }
129
+ export function runGetRegistryDocument(ctx, args) {
130
+ if (args.kind === "capability-manifest") {
131
+ const document = ctx.project.manifests[args.id];
132
+ if (document === undefined) {
133
+ return fail("unknown_reference", `No capability manifest named "${args.id}" was found.`, { kind: args.kind, id: args.id });
134
+ }
135
+ return ok(`Loaded capability manifest "${args.id}".`, { kind: args.kind, id: args.id, document });
136
+ }
137
+ const document = getRegistryDocument(ctx.project.registry, args.kind, args.id);
138
+ if (document === undefined) {
139
+ return fail("unknown_reference", `No ${args.kind} named "${args.id}" was found.`, { kind: args.kind, id: args.id });
140
+ }
141
+ return ok(`Loaded ${args.kind} "${args.id}".`, { kind: args.kind, id: args.id, document });
142
+ }
143
+ export function runValidateDocument(ctx, args) {
144
+ const document = parseDocument(args.document);
145
+ if (maybeFail(document))
146
+ return toolFail(document);
147
+ const result = ctx.validator.validate(document, args.kind);
148
+ return ok(result.valid ? "Document is valid." : "Document is invalid.", { valid: result.valid, errors: result.errors });
149
+ }
150
+ export function runValidateRegistry(ctx) {
151
+ const diagnostics = [...ctx.loadDiagnostics, ...registryDiagnostics(ctx.project, ctx.validator)];
152
+ return ok(diagnostics.length === 0 ? "Registry is valid." : `Registry has ${diagnostics.length} diagnostic(s).`, {
153
+ valid: diagnostics.length === 0,
154
+ diagnostics,
155
+ });
156
+ }
157
+ export function runResolveAesthetic(ctx, args) {
158
+ const input = { aesthetic: args.aesthetic };
159
+ if (args.merge_strategies !== undefined)
160
+ input.merge_strategies = args.merge_strategies;
161
+ const resolved = resolveInput(ctx, input);
162
+ if (maybeFail(resolved))
163
+ return toolFail(resolved);
164
+ const id = typeof args.aesthetic === "string" ? args.aesthetic : "inline";
165
+ return ok(`Resolved aesthetic "${id}".`, { canonical: resolved.canonical });
166
+ }
167
+ export function runCompileDesignTokens(ctx, args) {
168
+ const resolved = resolveInput(ctx, args);
169
+ if (maybeFail(resolved))
170
+ return toolFail(resolved);
171
+ const tokenOptions = {};
172
+ if (args.preserve_unknown !== undefined)
173
+ tokenOptions.preserveUnknown = args.preserve_unknown;
174
+ const cssOptions = {};
175
+ if (args.css_prefix !== undefined)
176
+ cssOptions.prefix = args.css_prefix;
177
+ const tokens = compileDesignTokens(resolved.canonical, tokenOptions);
178
+ const css_variables = toCssVariables(tokens, cssOptions);
179
+ return ok("Compiled portable design tokens.", { source: resolved.source, canonical: resolved.canonical, tokens, css_variables });
180
+ }
181
+ export function runCompileProviderPayload(ctx, args) {
182
+ const input = { aesthetic: args.aesthetic };
183
+ if (args.merge_strategies !== undefined)
184
+ input.merge_strategies = args.merge_strategies;
185
+ const resolved = resolveInput(ctx, input);
186
+ if (maybeFail(resolved))
187
+ return toolFail(resolved);
188
+ const adapter = ctx.adapters?.[args.provider];
189
+ const manifest = ctx.project.manifests[args.provider] ?? adapter?.manifest();
190
+ if (manifest === undefined)
191
+ return fail("unknown_provider", `No provider manifest or adapter named "${args.provider}" was found.`, { provider: args.provider });
192
+ const manifestValidation = ctx.validator.validate(manifest, "capability-manifest");
193
+ if (!manifestValidation.valid) {
194
+ return fail("schema_failure", `Provider manifest "${args.provider}" failed schema validation.`, { errors: manifestValidation.errors });
195
+ }
196
+ const compiled = compile(resolved.canonical, manifest, args.criticality ?? {});
197
+ if (compiled.status === "error") {
198
+ return ok(`Provider "${args.provider}" cannot satisfy required construct "${compiled.construct}".`, {
199
+ status: "error",
200
+ error: compiled.error,
201
+ construct: compiled.construct,
202
+ canonical: resolved.canonical,
203
+ });
204
+ }
205
+ if (adapter === undefined) {
206
+ return ok(`Compiled provider contract for "${args.provider}".`, {
207
+ status: "compiled",
208
+ provider: args.provider,
209
+ warnings: compiled.warnings,
210
+ canonical: resolved.canonical,
211
+ });
212
+ }
213
+ try {
214
+ const emission = adapter.emit(resolved.canonical, { criticality: args.criticality ?? {} });
215
+ return ok(`Compiled deterministic provider payload for "${args.provider}".`, {
216
+ status: "compiled",
217
+ provider: args.provider,
218
+ warnings: emission.warnings,
219
+ canonical: resolved.canonical,
220
+ emission,
221
+ });
222
+ }
223
+ catch (e) {
224
+ const failure = runtimeError(e);
225
+ return toolFail(failure);
226
+ }
227
+ }
228
+ export function runDraftAesthetic(_ctx, args) {
229
+ const overrides = args.overrides === undefined ? undefined : parseDocument(args.overrides);
230
+ if (overrides !== undefined && maybeFail(overrides))
231
+ return toolFail(overrides);
232
+ try {
233
+ const options = {};
234
+ if (args.modifiers !== undefined)
235
+ options.modifiers = args.modifiers;
236
+ if (overrides !== undefined)
237
+ options.overrides = overrides;
238
+ const document = createComposition(args.id, args.base, options);
239
+ return ok(`Drafted aesthetic "${args.id}".`, { kind: "composition", id: args.id, document });
240
+ }
241
+ catch (e) {
242
+ const failure = runtimeError(e);
243
+ return toolFail(failure);
244
+ }
245
+ }
246
+ export function runSaveAesthetic(ctx, args) {
247
+ const disabled = writeDisabled(ctx);
248
+ if (disabled !== undefined)
249
+ return toolFail(disabled);
250
+ const draft = runDraftAesthetic(ctx, args);
251
+ if (draft.isError === true)
252
+ return draft;
253
+ const document = draft.structuredContent?.document;
254
+ if (document === undefined)
255
+ return fail("invalid_document", "Drafted aesthetic did not produce a document.");
256
+ const validation = validateForSave(ctx, document, "composition");
257
+ if (validation !== undefined)
258
+ return toolFail(validation);
259
+ try {
260
+ saveProjectDocument(ctx.registryDir, "composition", document);
261
+ reloadContext(ctx);
262
+ return ok(`Saved aesthetic "${args.id}".`, { kind: "composition", id: args.id, document });
263
+ }
264
+ catch (e) {
265
+ return toolFail(runtimeError(e));
266
+ }
267
+ }
268
+ export function runSaveRegistryDocument(ctx, args) {
269
+ const disabled = writeDisabled(ctx);
270
+ if (disabled !== undefined)
271
+ return toolFail(disabled);
272
+ const document = parseDocument(args.document);
273
+ if (maybeFail(document))
274
+ return toolFail(document);
275
+ const validation = validateForSave(ctx, document, args.kind);
276
+ if (validation !== undefined)
277
+ return toolFail(validation);
278
+ try {
279
+ if (args.kind === "capability-manifest") {
280
+ const manifest = document;
281
+ saveCapabilityManifest(ctx.registryDir, manifest);
282
+ reloadContext(ctx);
283
+ return ok(`Saved capability manifest "${manifest.provider}".`, { kind: args.kind, id: manifest.provider, document });
284
+ }
285
+ saveProjectDocument(ctx.registryDir, args.kind, document);
286
+ reloadContext(ctx);
287
+ return ok(`Saved ${args.kind} "${String(document.id)}".`, { kind: args.kind, id: document.id, document });
288
+ }
289
+ catch (e) {
290
+ return toolFail(runtimeError(e));
291
+ }
292
+ }
293
+ export function runDeleteRegistryDocument(ctx, args) {
294
+ const disabled = writeDisabled(ctx);
295
+ if (disabled !== undefined)
296
+ return toolFail(disabled);
297
+ try {
298
+ if (args.kind === "capability-manifest") {
299
+ const deleted = deleteCapabilityManifest(ctx.registryDir, args.id);
300
+ reloadContext(ctx);
301
+ return ok(deleted ? `Deleted capability manifest "${args.id}".` : `Capability manifest "${args.id}" did not exist.`, {
302
+ kind: args.kind,
303
+ id: args.id,
304
+ deleted,
305
+ });
306
+ }
307
+ deleteProjectDocument(ctx.registryDir, args.kind, args.id);
308
+ reloadContext(ctx);
309
+ return ok(`Deleted ${args.kind} "${args.id}" if it existed.`, { kind: args.kind, id: args.id, deleted: true });
310
+ }
311
+ catch (e) {
312
+ return toolFail(runtimeError(e));
313
+ }
314
+ }
315
+ export function runWriteMergeStrategies(ctx, args) {
316
+ const disabled = writeDisabled(ctx);
317
+ if (disabled !== undefined)
318
+ return toolFail(disabled);
319
+ try {
320
+ writeMergeStrategies(ctx.registryDir, args.merge_strategies);
321
+ reloadContext(ctx);
322
+ return ok("Saved merge strategies.", { merge_strategies: args.merge_strategies });
323
+ }
324
+ catch (e) {
325
+ return toolFail(runtimeError(e));
326
+ }
327
+ }
328
+ export function errorCodeFromResult(result) {
329
+ const code = result.structuredContent?.error;
330
+ return typeof code === "string" ? code : undefined;
331
+ }
@@ -0,0 +1,12 @@
1
+ import { type CapabilityManifest, type Criticality, type MergeStrategies, type Registry } from "@mosvera/runtime";
2
+ import type { CompileResultMcp, ToolContext } from "../types.ts";
3
+ export interface CompileArgs {
4
+ composition: object | string;
5
+ provider: string;
6
+ registry?: Registry;
7
+ manifest?: CapabilityManifest;
8
+ criticality?: Record<string, Criticality>;
9
+ merge_strategies?: MergeStrategies;
10
+ emit?: boolean;
11
+ }
12
+ export declare function runCompileGeneration(ctx: ToolContext, args: CompileArgs): CompileResultMcp;
@@ -0,0 +1,67 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ //
3
+ // compile_generation tool handler. Resolves a composition, then runs the
4
+ // MEP-0003 compilation contract (compile) against a provider capability
5
+ // manifest sourced inline, from the loaded registry, or from an injected Phase
6
+ // 4 adapter. When emit=true and an adapter is available, returns the
7
+ // deterministic provider payload emission. No provider HTTP call is made.
8
+ import { compile, parse, resolveComposition, } from "@mosvera/runtime";
9
+ import { EmissionError } from "@mosvera/provider-base";
10
+ import { invalidDocument, isResolutionError, mapResolutionError, toolError } from "../errors.js";
11
+ import { collectMissingReferences } from "../registry/preflight.js";
12
+ import { composeStrategies, mergeRegistry } from "../registry/strategies.js";
13
+ export function runCompileGeneration(ctx, args) {
14
+ let composition;
15
+ try {
16
+ composition = parse(args.composition);
17
+ }
18
+ catch (e) {
19
+ const message = e instanceof Error ? e.message : String(e);
20
+ return invalidDocument([{ path: "", message: `parse error: ${message}` }]);
21
+ }
22
+ const cv = ctx.validator.validate(composition, "composition");
23
+ if (!cv.valid)
24
+ return invalidDocument(cv.errors);
25
+ const registry = mergeRegistry(ctx.project.registry, args.registry);
26
+ const missing = collectMissingReferences(composition, registry);
27
+ if (missing.length > 0) {
28
+ return toolError("unknown_reference", { missing });
29
+ }
30
+ const adapter = ctx.adapters?.[args.provider];
31
+ const manifest = args.manifest ?? ctx.project.manifests[args.provider] ?? adapter?.manifest();
32
+ if (manifest === undefined) {
33
+ return toolError("unknown_provider", { provider: args.provider });
34
+ }
35
+ const mv = ctx.validator.validate(manifest, "capability-manifest");
36
+ if (!mv.valid)
37
+ return invalidDocument(mv.errors);
38
+ const strategies = composeStrategies(ctx.baseStrategies, args.merge_strategies);
39
+ let canonical;
40
+ try {
41
+ canonical = resolveComposition(composition, registry, strategies);
42
+ }
43
+ catch (e) {
44
+ if (isResolutionError(e))
45
+ return mapResolutionError(e);
46
+ throw e;
47
+ }
48
+ const result = compile(canonical, manifest, args.criticality ?? {});
49
+ if (result.status === "error") {
50
+ return { status: "error", error: result.error, construct: result.construct, canonical };
51
+ }
52
+ if (args.emit === true) {
53
+ if (adapter === undefined) {
54
+ return toolError("unknown_provider", { provider: args.provider, adapter: "missing" });
55
+ }
56
+ try {
57
+ return { status: "compiled", canonical, emission: adapter.emit(canonical, { criticality: args.criticality ?? {} }) };
58
+ }
59
+ catch (e) {
60
+ if (e instanceof EmissionError) {
61
+ return { status: "error", error: e.error, construct: e.construct, canonical };
62
+ }
63
+ throw e;
64
+ }
65
+ }
66
+ return { status: "compiled", warnings: result.warnings, canonical };
67
+ }
@@ -0,0 +1,11 @@
1
+ import type { JsonObject, Registry } from "@mosvera/runtime";
2
+ import type { ToolContext, ToolError } from "../types.ts";
3
+ export interface GetPaletteArgs {
4
+ name: string;
5
+ registry?: Registry;
6
+ }
7
+ export type GetPaletteResult = {
8
+ palette: JsonObject;
9
+ inheritance_unresolved?: true;
10
+ } | ToolError;
11
+ export declare function runGetPalette(ctx: ToolContext, args: GetPaletteArgs): GetPaletteResult;
@@ -0,0 +1,23 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ //
3
+ // get_palette tool handler. Returns the named palette as stored (schema-
4
+ // validated). Palette $extends resolution is deferred at v0.1 (design spec
5
+ // D4): if the palette declares $extends, the response carries
6
+ // inheritance_unresolved:true so a consumer is never silently misled.
7
+ import { invalidDocument, toolError } from "../errors.js";
8
+ import { mergeRegistry } from "../registry/strategies.js";
9
+ export function runGetPalette(ctx, args) {
10
+ const reg = mergeRegistry(ctx.project.registry, args.registry);
11
+ const palettes = reg.palettes ?? {};
12
+ if (!(args.name in palettes)) {
13
+ return toolError("unknown_reference", { name: args.name });
14
+ }
15
+ const palette = palettes[args.name];
16
+ const res = ctx.validator.validate(palette, "palette");
17
+ if (!res.valid)
18
+ return invalidDocument(res.errors);
19
+ if (typeof palette["$extends"] === "string") {
20
+ return { palette, inheritance_unresolved: true };
21
+ }
22
+ return { palette };
23
+ }