Skip to content

Commit 4d8d7f6

Browse files
authored
Merge pull request #2794 from SalesforceCommerceCloud/bendvc/revert-create-app-changes
[🧱 MCP Server Tool] Create App Tool Polish 💅
2 parents da8081a + 51b8d19 commit 4d8d7f6

File tree

11 files changed

+206
-76
lines changed

11 files changed

+206
-76
lines changed

packages/pwa-kit-create-app/program.json

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{
2+
"_ai": {
3+
"instruction": "This file defines the CLI behavior for generating React projects using templates and presets. AI agents can use this to guide interactive or programmatic project creation.",
4+
"expectedUse": "Provides schema and data for decision making during app generation."
5+
},
26
"$schema": "http://json-schema.org/draft-07/schema#",
37
"metadata": {
48
"name": "pwa-kit-create-app",
@@ -22,13 +26,21 @@
2226
],
2327
"properties": {
2428
"name": {
25-
"type": "string"
29+
"type": "string",
30+
"_ai": {
31+
"instruction": "This value maps directly to the key in the answers object.",
32+
"expectedUse": "Use this to store or retrieve the corresponding answer."
33+
}
2634
},
2735
"message": {
2836
"type": "string"
2937
},
3038
"type": {
31-
"type": "string"
39+
"type": "string",
40+
"_ai": {
41+
"instruction": "This defines the prompt type (e.g., list, input, confirm). Determines how to display the question.",
42+
"expectedUse": "Configure the appropriate UI for asking the question."
43+
}
3244
},
3345
"choices": {
3446
"type": "array",
@@ -44,10 +56,18 @@
4456
},
4557
"value": {}
4658
}
59+
},
60+
"_ai": {
61+
"instruction": "Only used when type is 'list', 'rawlist', 'checkbox', or similar. These are the selectable options.",
62+
"expectedUse": "Render multiple choice options to the user."
4763
}
4864
}
4965
},
5066
"additionalProperties": true
67+
},
68+
"_ai": {
69+
"instruction": "Defines the questions to be asked to the user or AI agent to gather required input for project generation.",
70+
"expectedUse": "Used to drive interactive or programmatic question-and-answer flows."
5171
}
5272
},
5373
"presets": {
@@ -80,12 +100,16 @@
80100
},
81101
"answers": {
82102
"type": "object",
83-
"additionalProperties": {}
103+
"additionalProperties": {},
104+
"_ai": {
105+
"instruction": "These are default answers pre-filled when this preset is selected. They should be merged with user-supplied answers, where user-supplied values take precedence.",
106+
"expectedUse": "Auto-filling answers to questions without prompting the user unless a value is missing or overridden."
107+
}
84108
},
85109
"private": {
86110
"type": "boolean",
87111
"_ai": {
88-
"instruction": "This property is used to filter what presets are shown to the user. If the preset is private, should NOT be shown to the user in a list of selectable presets. This equates to the 'when' property Inquirer question schema.",
112+
"instruction": "This property is used to filter what presets are shown to the user. If the preset is private, it should NOT be shown to the user in a list of selectable presets.",
89113
"expectedUse": "Hiding presets from the user."
90114
}
91115
}
@@ -142,7 +166,11 @@
142166
},
143167
"required": [
144168
"type"
145-
]
169+
],
170+
"_ai": {
171+
"instruction": "This defines how the template is sourced. If 'npm', then 'name' must be the NPM package name. If 'bundle', the template is included directly in the CLI.",
172+
"expectedUse": "Used to determine how to fetch and prepare the template for project creation."
173+
}
146174
},
147175
"questions": {
148176
"$ref": "questions.json",
@@ -159,7 +187,11 @@
159187
},
160188
"answers": {
161189
"type": "object",
162-
"additionalProperties": {}
190+
"additionalProperties": {},
191+
"_ai": {
192+
"instruction": "These are default answers pre-filled when this template is selected. They should be merged with user-supplied answers, where user-supplied values take precedence.",
193+
"expectedUse": "Auto-filling answers to questions without prompting the user unless a value is missing or overridden."
194+
}
163195
},
164196
"private": {
165197
"type": "boolean",
@@ -217,6 +249,10 @@
217249
"message",
218250
"regex"
219251
]
252+
},
253+
"_ai": {
254+
"instruction": "These validators can be applied to template questions to ensure valid input. Each validator has a unique `id` that is referenced from questions.",
255+
"expectedUse": "Validating user input for questions during project creation."
220256
}
221257
},
222258
"options": {
@@ -237,7 +273,15 @@
237273
"required": [
238274
"name",
239275
"description"
240-
]
276+
],
277+
"_ai": {
278+
"instruction": "This option modifies the behavior of the CLI tool. Use `defaultValue` if the option is not provided.",
279+
"expectedUse": "Determine which CLI flags can be included when generating the command."
280+
}
281+
},
282+
"_ai": {
283+
"instruction": "These are the options/arguments that can be passed to the @salesforce/pwa-kit-create-app CLI tool. Do not use options that aren't listed here.",
284+
"expectedUse": "Instruct agent on how to use the @salesforce/pwa-kit-create-app CLI tool to create a new PWA Kit project."
241285
}
242286
},
243287
"examples": {
@@ -255,7 +299,11 @@
255299
"required": [
256300
"description",
257301
"command"
258-
]
302+
],
303+
"_ai": {
304+
"instruction": "Each example shows how to use the CLI tool with real command-line input.",
305+
"expectedUse": "Show examples in documentation or when the agent is generating usage examples."
306+
}
259307
}
260308
}
261309
},
@@ -692,4 +740,4 @@
692740
}
693741
]
694742
}
695-
}
743+
}

packages/pwa-storefront-mcp/jest.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
*/
77
// eslint-disable-next-line @typescript-eslint/no-var-requires
88
const parentConfig = require('@salesforce/pwa-kit-dev/configs/jest/jest.config.js')
9+
// eslint-disable-next-line @typescript-eslint/no-var-requires
10+
const path = require('path')
911

1012
module.exports = {
1113
...parentConfig,
1214
testEnvironment: 'node',
1315
testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js'],
1416
testPathIgnorePatterns: ['bin/*', 'coverage/*', 'dist/*', 'node_modules/*', 'scripts/*'],
17+
setupFilesAfterEnv: [path.join(__dirname, 'jest-setup.js')],
1518
collectCoverageFrom: ['src/**/*.js', '!src/**/*.test.js', '!src/**/*.spec.js'],
1619
coverageDirectory: 'coverage',
1720
coverageReporters: ['text', 'lcov', 'html'],

packages/pwa-storefront-mcp/package-lock.json

Lines changed: 0 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/pwa-storefront-mcp/src/server/server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class PwaStorefrontMCPServerHighLevel {
9595
this.sessions[sessionId] = {step: 1, answers: {}}
9696
}
9797
const session = this.sessions[sessionId]
98-
const {step, answers} = session
98+
const {step} = session
9999
const answer = args.answer?.trim()
100100
switch (step) {
101101
case 1:

packages/pwa-storefront-mcp/src/server/server.test.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ describe('PwaStorefrontMCPServerHighLevel integration', () => {
3737
it('should list registered tools via stdio', async () => {
3838
const child = spawn(BABEL_NODE_PATH, ['src/server/server.js'], {
3939
cwd: process.cwd(),
40-
stdio: ['pipe', 'pipe', 'inherit']
40+
stdio: ['pipe', 'pipe', 'inherit'],
41+
env: {
42+
...process.env,
43+
// NOTE: THIS ENV VAR IS USUALLY SET BY CURSOR OR THE MCP SERVER?
44+
WORKSPACE_FOLDER_PATHS: path.resolve(process.cwd(), '..', '..')
45+
}
4146
})
4247

4348
// Wait a moment for the server to start

packages/pwa-storefront-mcp/src/utils/create-new-component-tool.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ export default ${pascalComponentName};
139139
const componentDir = path.join(location, kebabDirName)
140140
await fs.mkdir(componentDir, {recursive: true})
141141
const componentFilePath = path.join(componentDir, 'index.jsx')
142-
const fields = Object.keys(dataModel)
143142
let code = ''
144143

145144
// Special logic for product entity

packages/pwa-storefront-mcp/src/utils/create-new-component-tool.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
*/
77
import CreateNewComponentTool from './create-new-component-tool.js'
88
import * as fs from 'fs/promises'
9-
import path from 'path'
109

1110
// Mock fs/promises to avoid actual file operations
1211
jest.mock('fs/promises', () => ({

packages/pwa-storefront-mcp/src/utils/pwa-create-app-guideline-tool.js

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,13 @@
44
* SPDX-License-Identifier: BSD-3-Clause
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
7-
import os from 'os'
8-
import path from 'path'
9-
import {exec} from 'child_process'
10-
import fs from 'fs/promises'
117

128
// Project dependencies
13-
import {EmptyJsonSchema, runNpxCommand} from './utils'
9+
import {EmptyJsonSchema, getCreateAppCommand, isMonoRepo, runCommand} from './utils'
1410

15-
//const CREATE_APP_VERSION = 'latest'
16-
//const CREATE_APP_VERSION = '3.11.0-nightly-20250630080227'
17-
const CREATE_APP_COMMAND = '@salesforce/pwa-kit-create-app@3.11.0-nightly-20250630080227'
18-
const DISPLAY_PROGRAM_COMMAND = '--displayProgram'
19-
const NPX_COMMAND = 'npx'
11+
const CREATE_APP_COMMAND = getCreateAppCommand()
12+
const DISPLAY_PROGRAM_FLAG = '--displayProgram'
13+
const COMMAND_RUNNER = isMonoRepo() ? 'node' : 'npx'
2014

2115
const guidelinesText = `
2216
# PWA Kit Create App — Agent Usage Guidelines
@@ -63,26 +57,21 @@ If the user requests a project using a **template**:
6357
- Never attempt to create a project without using this tool.
6458
- When gathering answers for a template, ask questions one at a time to maintain clarity.
6559
- Presets and templates are mutually exclusive paths. Do not offer both options unless explicitly requested.
66-
- Use the \`${NPX_COMMAND}\` command to run the \`${CREATE_APP_COMMAND}\` CLI tool when creating a new project.
60+
- Do not pass any flags to the \`${CREATE_APP_COMMAND}\` CLI tool that are not listed in the program.json options".
61+
- Use the \`${COMMAND_RUNNER}\` command to run the \`${CREATE_APP_COMMAND}\` CLI tool when creating a new project.
6762
`
6863

6964
export default {
7065
name: 'create_app_guidelines',
7166
description: `This tool is used to provide the agent with the instructions on how to use the @salesforce/pwa-kit-create-app CLI tool to create a new PWA Kit projects. Do not attempt to create a project without using this tool first.`,
7267
inputSchema: EmptyJsonSchema,
7368
fn: async () => {
74-
let programOutput = ''
75-
7669
// Run the display program and get the output.
77-
try {
78-
programOutput = await runNpxCommand(
79-
NPX_COMMAND,
80-
CREATE_APP_COMMAND,
81-
DISPLAY_PROGRAM_COMMAND
82-
)
83-
} catch (err) {
84-
console.error('Failed to run display program:', err)
85-
}
70+
const programOutput = await runCommand(COMMAND_RUNNER, [
71+
...(COMMAND_RUNNER === 'npx' ? ['--yes'] : []),
72+
CREATE_APP_COMMAND,
73+
DISPLAY_PROGRAM_FLAG
74+
])
8675

8776
// Parse the output and get the data, metadata, and schemas.
8877
const {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2025, Salesforce, Inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
import CreateAppGuidelineTool from './pwa-create-app-guideline-tool'
8+
import {EmptyJsonSchema} from './utils'
9+
10+
jest.mock('./utils', () => {
11+
const originalModule = jest.requireActual('./utils')
12+
// eslint-disable-next-line @typescript-eslint/no-var-requires
13+
const path = require('path')
14+
const mockScriptPath = path.resolve('../pwa-kit-create-app/scripts/create-mobify-app.js')
15+
16+
return {
17+
...originalModule,
18+
isMonoRepo: jest.fn(() => true),
19+
getCreateAppCommand: jest.fn(() => mockScriptPath)
20+
}
21+
})
22+
23+
describe('PWA Create App Guidelines', () => {
24+
describe('CreateAppGuidelineTool', () => {
25+
it('should have correct structure', () => {
26+
expect(CreateAppGuidelineTool).toMatchObject({
27+
name: 'create_app_guidelines',
28+
description: `This tool is used to provide the agent with the instructions on how to use the @salesforce/pwa-kit-create-app CLI tool to create a new PWA Kit projects. Do not attempt to create a project without using this tool first.`,
29+
inputSchema: EmptyJsonSchema,
30+
fn: expect.any(Function)
31+
})
32+
})
33+
34+
it('should return guidelines content when executed', async () => {
35+
// NOTE: THIS TEST IS SIMPLY A SANITY CHECK TO ENSURE THE TOOL IS WORKING.
36+
// IT DOES NOT TEST THE CONTENT OF THE GUIDELINES IN ITS ENTIRETY.
37+
const result = await CreateAppGuidelineTool.fn()
38+
39+
expect(result).toEqual({
40+
content: [
41+
{
42+
type: 'text',
43+
text: expect.stringContaining('PWA Kit Create App — Agent Usage Guidelines')
44+
}
45+
]
46+
})
47+
})
48+
49+
it('should include all major sections in the guidelines', async () => {
50+
const result = await CreateAppGuidelineTool.fn()
51+
const guidelineText = result.content[0].text
52+
53+
const requiredSections = [
54+
'Overview',
55+
'General Rules',
56+
'Creating a Project Using a Preset',
57+
'Creating a Project Using a Template',
58+
'Important Reminders'
59+
]
60+
61+
requiredSections.forEach((section) => {
62+
expect(guidelineText).toContain(section)
63+
})
64+
})
65+
})
66+
})
67+
68+
afterAll(() => {
69+
delete process.env.WORKSPACE_FOLDER_PATHS
70+
})

0 commit comments

Comments
 (0)