Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/early-clubs-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@salesforce/b2c-dx-mcp': patch
'@salesforce/b2c-dx-docs': patch
---

MCP MRT Push now uses correct defaults based on detected project type
28 changes: 26 additions & 2 deletions docs/mcp/tools/mrt-bundle-push.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,38 @@ Requires Managed Runtime (MRT) credentials. See [MRT Credentials](../configurati

## Parameters

Defaults for `buildDirectory`, `ssrOnly`, and `ssrShared` are chosen by detected project type (Storefront Next, PWA Kit v3, or generic). Explicit parameters override the project-type defaults.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `buildDirectory` | string | No | `./build` | Path to build directory containing the built project files. Can be absolute or relative to the project directory. |
| `message` | string | No | None | Deployment message to include with the bundle push. Useful for tracking deployments. |
| `ssrOnly` | string | No | `ssr.js,ssr.mjs,server/**/*` | Comma-separated glob patterns for server-only files (SSR). These files are only included in the server bundle. |
| `ssrShared` | string | No | `static/**/*,client/**/*` | Comma-separated glob patterns for shared files. These files are included in both server and client bundles. |
| `ssrOnly` | string | No | Varies by project type | Glob patterns for server-only files (SSR), comma-separated or JSON array. These files are only included in the server bundle. |
| `ssrShared` | string | No | Varies by project type | Glob patterns for shared files, comma-separated or JSON array. These files are included in both server and client bundles. |
| `deploy` | boolean | No | `false` | Whether to deploy to an environment after push. When `true`, `environment` must be provided via `--environment` flag or `MRT_ENVIRONMENT`. |

### Default values by project type

When `buildDirectory`, `ssrOnly`, or `ssrShared` are omitted, the tool detects the project type and applies these defaults:

**Generic** (used when no project type is detected; matches CLI `b2c mrt bundle deploy` defaults):

- `buildDirectory`: `./build`
- `ssrOnly`: `ssr.js`, `ssr.mjs`, `server/**/*`
- `ssrShared`: `static/**/*`, `client/**/*`

**PWA Kit v3**:

- `buildDirectory`: `./build`
- `ssrOnly`: `ssr.js`, `ssr.js.map`, `node_modules/**/*.*`
- `ssrShared`: `static/ico/favicon.ico`, `static/robots.txt`, `**/*.js`, `**/*.js.map`, `**/*.json`

**Storefront Next**:

- `buildDirectory`: `./build`
- `ssrOnly`: `server/**/*`, `loader.js`, `streamingHandler.{js,mjs,cjs}`, `streamingHandler.{js,mjs,cjs}.map`, `ssr.{js,mjs,cjs}`, `ssr.{js,mjs,cjs}.map`, `!static/**/*`, `sfnext-server-*.mjs`, plus exclusions for Storybook and test files
- `ssrShared`: `client/**/*`, `static/**/*`, `**/*.css`, image/font extensions, plus exclusions for Storybook and test files

## Usage Examples

### Push Bundle Only
Expand Down
131 changes: 124 additions & 7 deletions packages/b2c-dx-mcp/src/tools/mrt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,106 @@ import {createToolAdapter, jsonResult} from '../adapter.js';
import {pushBundle} from '@salesforce/b2c-tooling-sdk/operations/mrt';
import type {PushResult, PushOptions} from '@salesforce/b2c-tooling-sdk/operations/mrt';
import type {AuthStrategy} from '@salesforce/b2c-tooling-sdk/auth';
import {detectWorkspaceType, type ProjectType} from '@salesforce/b2c-tooling-sdk/discovery';
import {getLogger} from '@salesforce/b2c-tooling-sdk/logging';

/**
* Parses a glob pattern string into an array of patterns.
* Accepts either a JSON array (e.g. '["server/**\/*", "ssr.{js,mjs}"]')
* or a comma-separated string (e.g. 'server/**\/*,ssr.js').
* JSON array format supports brace expansion in individual patterns.
*/
function parseGlobPatterns(value: string): string[] {
const trimmed = value.trim();
if (trimmed.startsWith('[')) {
const parsed: unknown = JSON.parse(trimmed);
if (!Array.isArray(parsed) || !parsed.every((item) => typeof item === 'string')) {
throw new Error('Invalid glob pattern array: expected an array of strings');
}
return parsed.map((s: string) => (s as string).trim()).filter(Boolean);
}
return trimmed
.split(',')
.map((s) => s.trim())
.filter(Boolean);
}

interface MrtDefaults {
ssrOnly: string[];
ssrShared: string[];
buildDirectory: string;
}

const MRT_DEFAULTS: Record<'default' | 'pwa-kit-v3' | 'storefront-next', MrtDefaults> = {
'storefront-next': {
// ssrEntryPoint is 'streamingHandler' (production + MRT_BUNDLE_TYPE!=='ssr') or 'ssr' otherwise.
// Include both patterns so the bundle works regardless of MRT_BUNDLE_TYPE / mode.
ssrOnly: [
'server/**/*',
'loader.js',
'streamingHandler.{js,mjs,cjs}',
'streamingHandler.{js,mjs,cjs}.map',
'ssr.{js,mjs,cjs}',
'ssr.{js,mjs,cjs}.map',
'!static/**/*',
'sfnext-server-*.mjs',
'!**/*.stories.tsx',
'!**/*.stories.ts',
'!**/*-snapshot.tsx',
'!.storybook/**/*',
'!storybook-static/**/*',
'!**/__mocks__/**/*',
'!**/__snapshots__/**/*',
],
ssrShared: [
'client/**/*',
'static/**/*',
'**/*.css',
'**/*.png',
'**/*.jpg',
'**/*.jpeg',
'**/*.gif',
'**/*.svg',
'**/*.ico',
'**/*.woff',
'**/*.woff2',
'**/*.ttf',
'**/*.eot',
'!**/*.stories.tsx',
'!**/*.stories.ts',
'!**/*-snapshot.tsx',
'!.storybook/**/*',
'!storybook-static/**/*',
'!**/__mocks__/**/*',
'!**/__snapshots__/**/*',
],
buildDirectory: 'build',
},
'pwa-kit-v3': {
ssrOnly: ['ssr.js', 'ssr.js.map', 'node_modules/**/*.*'],
ssrShared: ['static/ico/favicon.ico', 'static/robots.txt', '**/*.js', '**/*.js.map', '**/*.json'],
buildDirectory: 'build',
},
default: {
ssrOnly: ['ssr.js', 'ssr.mjs', 'server/**/*'],
ssrShared: ['static/**/*', 'client/**/*'],
buildDirectory: 'build',
},
};

/**
* Returns MRT bundle defaults for the given project types.
* For hybrid projects (multiple types detected), prefers storefront-next over pwa-kit-v3.
*
* @param projectTypes - Detected project types from workspace discovery
* @returns Defaults for ssrOnly, ssrShared, and buildDirectory
*/
function getDefaultsForProjectTypes(projectTypes: ProjectType[]): MrtDefaults {
if (projectTypes.includes('storefront-next')) return MRT_DEFAULTS['storefront-next'];
if (projectTypes.includes('pwa-kit-v3')) return MRT_DEFAULTS['pwa-kit-v3'];
return MRT_DEFAULTS.default;
}

/**
* Input type for mrt_bundle_push tool.
*/
Expand All @@ -43,6 +141,8 @@ interface MrtBundlePushInput {
interface MrtToolInjections {
/** Mock pushBundle function for testing */
pushBundle?: (options: PushOptions, auth: AuthStrategy) => Promise<PushResult>;
/** Mock detectWorkspaceType function for testing */
detectWorkspaceType?: (path: string) => Promise<{projectTypes: ProjectType[]}>;
}

/**
Expand All @@ -59,6 +159,7 @@ interface MrtToolInjections {
*/
function createMrtBundlePushTool(loadServices: () => Services, injections?: MrtToolInjections): McpTool {
const pushBundleFn = injections?.pushBundle || pushBundle;
const detectWorkspaceTypeFn = injections?.detectWorkspaceType ?? detectWorkspaceType;
return createToolAdapter<MrtBundlePushInput, PushResult>(
{
name: 'mrt_bundle_push',
Expand All @@ -69,16 +170,25 @@ function createMrtBundlePushTool(loadServices: () => Services, injections?: MrtT
// MRT operations use ApiKeyStrategy from MRT_API_KEY or ~/.mobify
requiresMrtAuth: true,
inputSchema: {
buildDirectory: z.string().optional().describe('Path to build directory (default: ./build)'),
buildDirectory: z
.string()
.optional()
.describe(
'Path to build directory. Defaults vary by project type: Storefront Next, PWA Kit v3, or generic (./build).',
),
message: z.string().optional().describe('Deployment message'),
ssrOnly: z
.string()
.optional()
.describe('Glob patterns for server-only files, comma-separated (default: ssr.js,ssr.mjs,server/**/*)'),
.describe(
'Glob patterns for server-only files (comma-separated or JSON array). Defaults vary by project type: Storefront Next, PWA Kit v3, or generic.',
),
ssrShared: z
.string()
.optional()
.describe('Glob patterns for shared files, comma-separated (default: static/**/*,client/**/*)'),
.describe(
'Glob patterns for shared files (comma-separated or JSON array). Defaults vary by project type: Storefront Next, PWA Kit v3, or generic.',
),
deploy: z
.boolean()
.optional()
Expand Down Expand Up @@ -111,10 +221,16 @@ function createMrtBundlePushTool(loadServices: () => Services, injections?: MrtT
// Get origin from --cloud-origin flag or mrtOrigin config (optional)
const origin = context.mrtConfig?.origin;

// Parse comma-separated glob patterns (same as CLI defaults)
const ssrOnly = (args.ssrOnly || 'ssr.js,ssr.mjs,server/**/*').split(',').map((s) => s.trim());
const ssrShared = (args.ssrShared || 'static/**/*,client/**/*').split(',').map((s) => s.trim());
const buildDirectory = context.services.resolveWithProjectDirectory(args.buildDirectory || 'build');
// Detect project type and get project-type-aware defaults
const projectDir = context.services.resolveWithProjectDirectory();
const {projectTypes} = await detectWorkspaceTypeFn(projectDir);
const defaults = getDefaultsForProjectTypes(projectTypes);

const ssrOnly = args.ssrOnly ? parseGlobPatterns(args.ssrOnly) : defaults.ssrOnly;
const ssrShared = args.ssrShared ? parseGlobPatterns(args.ssrShared) : defaults.ssrShared;
const buildDirectory = context.services.resolveWithProjectDirectory(
args.buildDirectory ?? defaults.buildDirectory,
);

// Log all computed variables before pushing bundle
const logger = getLogger();
Expand All @@ -125,6 +241,7 @@ function createMrtBundlePushTool(loadServices: () => Services, injections?: MrtT
origin,
buildDirectory,
message: args.message,
projectTypes,
ssrOnly,
ssrShared,
},
Expand Down
Loading
Loading