@cmpose/mcp 0.4.0 → 0.5.0

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
@@ -76,6 +76,13 @@ saving, deep links, and in-app review and update check.
76
76
  - **IDs:** `ids` takes your real AdMob (`admob`), RevenueCat (`revenueCat`) and Play Games +
77
77
  Game Center (`gameServices`) IDs, each group with its flag; `game_describe` lists the fields.
78
78
  A group you leave out keeps the test IDs / placeholders and is reported as a warning.
79
+ - **Look (`theme`):** one of the template's style families (`bright`, `neon`, `pastel`, `bold`,
80
+ `nature`, `clean`; palette, shapes, motion and a font pair), listed by `game_describe` under
81
+ `themes` with what each suits. `theme: { family, primary, accent, fontPair }`, every field
82
+ optional: `primary` and `accent` (`#RRGGBB`) recolour the family with their lightness shifted
83
+ until text and buttons keep their contrast; a colour that still cannot pass keeps the
84
+ family's and is reported as a warning. Without `theme` the project gets `clean`, exactly as
85
+ the template ships it.
79
86
  - **Your names everywhere:** package (application identifier), company name, app name, min SDK
80
87
  (25 to 36) and iOS deployment target (15.0 to 26.2).
81
88
 
@@ -98,14 +105,18 @@ as one Docker container behind nginx. `sharing` adds spaces, roles and invitatio
98
105
  It returns the project path, the template version it used, warnings (e.g. test ad IDs to
99
106
  replace) and next steps.
100
107
  - `game_describe`: the Unity game generator's engine and editor version, template types,
101
- renders, flags and their dependencies, ID groups, defaults and limits. Agents call it first.
108
+ renders, flags and their dependencies, ID groups, defaults, limits and the pinned template's
109
+ theme families (`themes`: `default` and each family's `id`, `title`, `suits` and `fontPairs`;
110
+ the first call downloads the template). Agents call it first.
102
111
  - `game_generate`: creates the game. Required: `projectName` (1 to 30 letters or digits,
103
112
  starting with a letter), `appName` (up to 30 characters), `packageName` (e.g.
104
113
  `com.acme.skyhopper`, at least 3 segments, up to 50 characters) and an absolute `outputDir`.
105
114
  Optional: `companyName` (like `projectName`; by default the package's second segment,
106
115
  capitalized), `templateType`, `render`, `flags`, `features` (module names: 1 to 20 lowercase
107
116
  letters or digits, starting with a letter), `minSdk` (25 to 36), `iosVersion` (15.0 to 26.2,
108
- written as `major.minor`), `ids`, `templateRef`. It never overwrites a non-empty folder.
117
+ written as `major.minor`), `ids`, `theme`, `templateRef`. It never overwrites a non-empty
118
+ folder. The result names the applied theme (`theme`) and lists colour fallbacks among the
119
+ warnings.
109
120
  - `backend_describe`: the backend generator's options, `appName` rules, the project anatomy and
110
121
  the conventions to follow when adding code (protected routes, the 404 rule, where features
111
122
  go, the security checklist, the local runner and the deploy parts). Agents call it first.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmpose/mcp",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "mcpName": "io.github.Keremturker/cmpose",
5
5
  "description": "MCP server that generates buildable Compose Multiplatform (Android + iOS) projects and Unity game projects locally from any AI agent",
6
6
  "license": "MIT",
@@ -43,7 +43,7 @@
43
43
  "test": "node --test test/*.test.js"
44
44
  },
45
45
  "dependencies": {
46
- "@cmpose/core": "0.4.0",
46
+ "@cmpose/core": "0.5.0",
47
47
  "@modelcontextprotocol/sdk": "1.32.1",
48
48
  "fs-extra": "^11.3.2",
49
49
  "zod": "3.25.76"
@@ -59,12 +59,14 @@ async function runGameGenerate(args, { cacheDir, remoteUrl, onProgress = () => {
59
59
  render: summary.render,
60
60
  flags: summary.flags,
61
61
  features: summary.features,
62
+ theme: summary.theme,
62
63
  warnings: summary.warnings,
63
64
  nextSteps: NEXT_STEPS_GAME
64
65
  };
65
66
  const text = [
66
67
  `Generated ${value.projectName} at ${target}`,
67
68
  `Template: ${source.repo}@${source.ref} (${String(source.commit).slice(0, 7)})`,
69
+ ...(result.theme ? [`Theme: ${result.theme.family} (${result.theme.fontPair})`] : []),
68
70
  ...result.warnings.map((w) => `Warning: ${w}`),
69
71
  'Next steps:', ...NEXT_STEPS_GAME.map((s) => `- ${s}`)
70
72
  ].join('\n');
@@ -75,4 +77,21 @@ async function runGameGenerate(args, { cacheDir, remoteUrl, onProgress = () => {
75
77
  }
76
78
  }
77
79
 
78
- module.exports = { runGameGenerate, NEXT_STEPS_GAME };
80
+ // game_describe with the pinned template's theme families (fetched into the cache on first use).
81
+ // A template that cannot be fetched or read still describes the generator: no families, and why.
82
+ async function runGameDescribe({ cacheDir, remoteUrl } = {}) {
83
+ let themes = null;
84
+ let unavailable = null;
85
+ try {
86
+ const cache = await core.templateCache.ensure({ ...core.pins.game, cacheDir, remoteUrl });
87
+ themes = await core.game.readThemes(cache.dir);
88
+ } catch (err) {
89
+ if (!(err instanceof core.TemplateFetchError || err instanceof TypeError || err instanceof core.game.ThemesFileError)) throw err;
90
+ unavailable = `Theme families unavailable: ${err.message}`;
91
+ }
92
+ const facts = core.game.describeGame({ themes });
93
+ if (unavailable) facts.themes.unavailable = unavailable;
94
+ return { content: [{ type: 'text', text: JSON.stringify(facts, null, 2) }], structuredContent: facts };
95
+ }
96
+
97
+ module.exports = { runGameGenerate, runGameDescribe, NEXT_STEPS_GAME };
package/src/server.js CHANGED
@@ -7,7 +7,7 @@ const core = require('@cmpose/core');
7
7
  const pkg = require('../package.json');
8
8
  const { describe } = require('./describe');
9
9
  const { runGenerate } = require('./generate');
10
- const { runGameGenerate } = require('./gameGenerate');
10
+ const { runGameGenerate, runGameDescribe } = require('./gameGenerate');
11
11
  const { runBackendGenerate } = require('./backendGenerate');
12
12
 
13
13
  const GENERATE_DESCRIPTION = 'Generate a Compose Multiplatform (Android + iOS) project on this machine. ' +
@@ -17,6 +17,11 @@ const GENERATE_DESCRIPTION = 'Generate a Compose Multiplatform (Android + iOS) p
17
17
  const GAME_GENERATE_DESCRIPTION = 'Generate a Unity 6 (Android + iOS) mobile game project on this machine. ' +
18
18
  'Call game_describe first for the flags, defaults and validation rules. ' +
19
19
  'showcase has every flag on; flags and features apply to blank only. ' +
20
+ 'theme picks one of the style families game_describe lists under themes (palette, shapes, motion, fonts; ' +
21
+ 'match the family whose suits fits the game) and optionally a fontPair of that family. primary and accent ' +
22
+ '(#RRGGBB) recolour the family: their lightness is shifted until text and buttons keep their contrast, and a ' +
23
+ 'colour that still cannot pass falls back to the family default with a warning. Without theme the template ' +
24
+ 'default family (clean) is used. ' +
20
25
  'Writes <outputDir>/<projectName>; never overwrites an existing non-empty folder.';
21
26
 
22
27
  const BACKEND_GENERATE_DESCRIPTION = 'Generate a standalone Kotlin/Ktor backend (Docker-ready) on this machine. ' +
@@ -70,12 +75,9 @@ function createServer({ cacheDir, remoteUrl, backendTemplateDir = process.env.CM
70
75
  }));
71
76
 
72
77
  server.registerTool('game_describe', {
73
- description: 'Describe the Unity game generator: engine and editor version, template types, renders, flags and their dependencies, ID groups, defaults, validation limits and the pinned game template.',
78
+ description: 'Describe the Unity game generator: engine and editor version, template types, renders, flags and their dependencies, ID groups, defaults, validation limits, the pinned game template and its theme families (themes: default, and each family\'s id, title, suits and fontPairs).',
74
79
  inputSchema: {}
75
- }, async () => {
76
- const facts = core.game.describeGame();
77
- return { content: [{ type: 'text', text: JSON.stringify(facts, null, 2) }], structuredContent: facts };
78
- });
80
+ }, async () => runGameDescribe({ cacheDir, remoteUrl }));
79
81
 
80
82
  server.registerTool('game_generate', {
81
83
  description: GAME_GENERATE_DESCRIPTION,
@@ -96,6 +98,14 @@ function createServer({ cacheDir, remoteUrl, backendTemplateDir = process.env.CM
96
98
  ids: z.record(z.string(), z.record(z.string(), z.string())).optional()
97
99
  .describe('Real IDs by group (admob, revenueCat, gameServices; fields in game_describe), each needing its flag. ' +
98
100
  'A group left out keeps the test IDs / placeholders, reported as a warning'),
101
+ // passthrough: unknown fields reach validateGame, which refuses them by name
102
+ theme: z.object({
103
+ family: z.string().optional().describe('A family id from game_describe themes.families (default: themes.default)'),
104
+ primary: z.string().optional().describe('#RRGGBB primary colour; lightness is adjusted to keep contrast'),
105
+ accent: z.string().optional().describe('#RRGGBB accent colour; lightness is adjusted to keep contrast'),
106
+ fontPair: z.string().optional().describe("One of the family's fontPairs (default: its first)")
107
+ }).passthrough().optional()
108
+ .describe('Style family and optional colour shifts; without it the template default family is used'),
99
109
  templateRef: z.string().optional().describe('Override the pinned game template tag')
100
110
  }
101
111
  }, async (args, extra) => runGameGenerate(args, {