Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
150 changes: 134 additions & 16 deletions packages/b2c-dx-mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ Since the package is not yet published to npm, see the [Development](#developmen
| `--tools` | Comma-separated individual tools to enable (case-insensitive) |
| `--allow-non-ga-tools` | Enable experimental (non-GA) tools |

#### Auth Flags

| Flag | Env Variable | Description |
|------|--------------|-------------|
| `--mrt-api-key` | `SFCC_MRT_API_KEY` | MRT API key for Managed Runtime operations |
| `--mrt-cloud-origin` | `SFCC_MRT_CLOUD_ORIGIN` | MRT cloud origin URL (default: https://cloud.mobify.com). See [Environment-Specific Config Files](#environment-specific-config-files) |

#### Global Flags (inherited from SDK)

| Flag | Description |
Expand All @@ -48,6 +55,13 @@ Since the package is not yet published to npm, see the [Development](#developmen
// Explicit config file path
"args": ["--toolsets", "all", "--config", "/path/to/dw.json"]

// MRT tools with API key
"args": ["--toolsets", "MRT", "--mrt-api-key", "your-api-key"]

// Or use environment variable in mcp.json
"args": ["--toolsets", "MRT"],
"env": { "SFCC_MRT_API_KEY": "your-api-key" }

// Enable experimental tools (required for placeholder tools)
"args": ["--toolsets", "all", "--allow-non-ga-tools"]

Expand Down Expand Up @@ -229,30 +243,134 @@ echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"cartridge_

> **Note:** Configuration is not currently required as all tools are placeholder implementations. This section will be relevant once tools are fully implemented.

Tools that interact with B2C Commerce instances (e.g., MRT, SCAPI, cartridge deployment) require credentials, which can be provided via **environment variables**, a **`.env` file**, a **`dw.json` file**, or the **`--config`** flag. Local tools (e.g., scaffolding, development guidelines) work without configuration.
Different tools require different types of configuration:

| Tool Type | Configuration Required |
|-----------|----------------------|
| **MRT tools** (e.g., `mrt_bundle_push`) | MRT API key |
| **B2C instance tools** (e.g., `cartridge_deploy`, SCAPI) | dw.json config |
| **Local tools** (e.g., scaffolding) | None |

#### MRT API Key

MRT (Managed Runtime) operations require an API key from the [Runtime Admin](https://runtime.commercecloud.com/) dashboard.

**Priority order** (highest to lowest):

1. Environment variables (`SFCC_*`) — includes `.env` file if present (shell env vars override `.env`)
2. `dw.json` file (auto-discovered or via `--config`)
1. `--mrt-api-key` flag
2. `SFCC_MRT_API_KEY` environment variable
3. `~/.mobify` config file (or `~/.mobify--[hostname]` if `--mrt-cloud-origin` is set)

#### Option 1: Environment Variables
**Option A: Flag or environment variable**

Set environment variables directly or create a `.env` file in your project root:
```json
// mcp.json - using flag
{
"mcpServers": {
"b2c-dx": {
"command": "b2c-dx-mcp",
"args": ["--toolsets", "MRT", "--mrt-api-key", "your-api-key"]
}
}
}

```bash
# .env file or shell exports
SFCC_HOSTNAME="your-sandbox.demandware.net"
SFCC_USERNAME="your.username"
SFCC_PASSWORD="your-access-key"
SFCC_CLIENT_ID="your-client-id"
SFCC_CLIENT_SECRET="your-client-secret"
SFCC_CODE_VERSION="version1"
// mcp.json - using env var
{
"mcpServers": {
"b2c-dx": {
"command": "b2c-dx-mcp",
"args": ["--toolsets", "MRT"],
"env": {
"SFCC_MRT_API_KEY": "your-api-key"
}
}
}
}
```

**Option B: ~/.mobify file (legacy)**

If you already use the `b2c` CLI for MRT operations, you may have a `~/.mobify` file configured:

```json
{
"username": "[email protected]",
"api_key": "your-api-key"
}
```

The MCP server will automatically use this file as a fallback if no flag or environment variable is set.

##### Environment-Specific Config Files

When using `~/.mobify` config files (i.e., no `--mrt-api-key` flag or `SFCC_MRT_API_KEY` env var), you can use `--mrt-cloud-origin` to select an environment-specific config file:

```json
// mcp.json - uses ~/.mobify--cloud-staging.mobify.com for API key
{
"mcpServers": {
"b2c-dx-staging": {
"command": "b2c-dx-mcp",
"args": ["--toolsets", "MRT", "--mrt-cloud-origin", "https://cloud-staging.mobify.com"]
}
}
}
```

| Cloud Origin | Config File |
|--------------|-------------|
| (default) | `~/.mobify` |
| `https://cloud-staging.mobify.com` | `~/.mobify--cloud-staging.mobify.com` |
| `https://cloud-dev.mobify.com` | `~/.mobify--cloud-dev.mobify.com` |

> **Note:** `--mrt-cloud-origin` is only relevant when the API key is resolved from a config file. If `--mrt-api-key` or `SFCC_MRT_API_KEY` is provided, this flag is ignored.

#### B2C Instance Config (dw.json)

Tools that interact with B2C Commerce instances (e.g., `cartridge_deploy`, SCAPI tools) require instance credentials.

**Priority order** (highest to lowest):

1. Environment variables (`SFCC_*`)
2. `dw.json` file (via `--config` flag or auto-discovery)

**Option A: Environment variables**

```json
{
"mcpServers": {
"b2c-dx": {
"command": "b2c-dx-mcp",
"args": ["--toolsets", "CARTRIDGES"],
"env": {
"SFCC_HOSTNAME": "your-sandbox.demandware.net",
"SFCC_USERNAME": "your.username",
"SFCC_PASSWORD": "your-access-key",
"SFCC_CLIENT_ID": "your-client-id",
"SFCC_CLIENT_SECRET": "your-client-secret",
"SFCC_CODE_VERSION": "version1"
}
}
}
}
```

**Option B: dw.json with explicit path**

```json
{
"mcpServers": {
"b2c-dx": {
"command": "b2c-dx-mcp",
"args": ["--toolsets", "CARTRIDGES", "--config", "/path/to/dw.json"]
}
}
}
```

#### Option 2: dw.json File
**Option C: dw.json with auto-discovery**

Create a `dw.json` file in your project root (auto-discovered by searching upward from current working directory):
Create a `dw.json` file in your project root. The MCP server will auto-discover it by searching upward from the current working directory:

```json
{
Expand All @@ -265,7 +383,7 @@ Create a `dw.json` file in your project root (auto-discovered by searching upwar
}
```

> **Note:** Environment variables take precedence over `dw.json` values.
> **Note:** Environment variables override values from `dw.json`. You can use env vars to override specific fields (e.g., secrets) while using dw.json for other settings.

## License

Expand Down
2 changes: 0 additions & 2 deletions packages/b2c-dx-mcp/bin/run.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
* - Uses compiled JavaScript from dist/
* - Loads .env file if present for local configuration
*
* Run directly: ./bin/run.js mcp --toolsets all
* Or with node: node bin/run.js mcp --toolsets all
*/

// Load .env file if present (Node.js native support)
Expand Down
133 changes: 99 additions & 34 deletions packages/b2c-dx-mcp/src/commands/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
* | `--tools` | Comma-separated individual tools to enable (case-insensitive) |
* | `--allow-non-ga-tools` | Enable experimental/non-GA tools |
*
* ### Auth Flags
* | Flag | Env Variable | Description |
* |------|--------------|-------------|
* | `--mrt-api-key` | `SFCC_MRT_API_KEY` | MRT API key for Managed Runtime operations |
* | `--mrt-cloud-origin` | `SFCC_MRT_CLOUD_ORIGIN` | MRT cloud origin URL for environment-specific ~/.mobify config |
*
* ### Global Flags (inherited from BaseCommand)
* | Flag | Description |
* |------|-------------|
Expand All @@ -30,50 +36,83 @@
* | `--json` | Output logs as JSON lines |
* | `--lang` | Language for messages |
*
* ## Configuration Priority
* ## Configuration
*
* Different tools require different configuration:
* - **MRT tools** (e.g., `mrt_bundle_push`) → MRT API key
* - **B2C instance tools** (e.g., `cartridge_deploy`, SCAPI) → dw.json config
* - **Local tools** (e.g., scaffolding) → None
*
* ### B2C Instance Configuration
* Priority (highest to lowest):
* 1. Environment variables (`SFCC_HOSTNAME`, `SFCC_USERNAME`, `SFCC_PASSWORD`, `SFCC_CLIENT_ID`, `SFCC_CLIENT_SECRET`, `SFCC_CODE_VERSION`)
* 2. dw.json file (via `--config` flag or auto-discovered by searching upward from cwd)
*
* 1. Environment variables (SFCC_*) - highest priority, override dw.json
* 2. dw.json file (explicit path via --config, or auto-discovered)
* 3. Auto-discovery (searches upward from cwd)
* ### MRT API Key
* Priority (highest to lowest):
* 1. `--mrt-api-key` flag
* 2. `SFCC_MRT_API_KEY` environment variable
* 3. `~/.mobify` config file (or `~/.mobify--[hostname]` if `--mrt-cloud-origin` is set)
*
* ## Toolset Validation
*
* - Invalid toolsets are ignored with a warning (server still starts)
* - If all toolsets are invalid, auto-discovery kicks in
*
* @example Start with all toolsets
* ```bash
* b2c-dx-mcp --toolsets all
* @example mcp.json - All toolsets
* ```json
* { "args": ["--toolsets", "all", "--allow-non-ga-tools"] }
* ```
*
* @example mcp.json - Specific toolsets
* ```json
* { "args": ["--toolsets", "CARTRIDGES,MRT", "--allow-non-ga-tools"] }
* ```
*
* @example Start with specific toolsets
* ```bash
* b2c-dx-mcp --toolsets CARTRIDGES,JOBS
* @example mcp.json - MRT tools with API key via env var
* ```json
* {
* "args": ["--toolsets", "MRT", "--allow-non-ga-tools"],
* "env": { "SFCC_MRT_API_KEY": "your-api-key" }
* }
* ```
*
* @example Start with specific individual tools
* ```bash
* b2c-dx-mcp --tools cartridge_deploy,cartridge_activate
* @example mcp.json - MRT tools with API key via flag
* ```json
* { "args": ["--toolsets", "MRT", "--mrt-api-key", "your-api-key", "--allow-non-ga-tools"] }
* ```
*
* @example Combine toolsets and specific tools
* ```bash
* b2c-dx-mcp --toolsets SCAPI --tools cartridge_deploy
* @example mcp.json - MRT tools with staging cloud origin (uses ~/.mobify--cloud-staging.mobify.com)
* ```json
* { "args": ["--toolsets", "MRT", "--mrt-cloud-origin", "https://cloud-staging.mobify.com", "--allow-non-ga-tools"] }
* ```
*
* @example Specify config file location
* ```bash
* b2c-dx-mcp --toolsets all --config /path/to/dw.json
* @example mcp.json - Cartridge tools with dw.json config
* ```json
* { "args": ["--toolsets", "CARTRIDGES", "--config", "/path/to/dw.json", "--allow-non-ga-tools"] }
* ```
*
* @example Enable debug logging
* ```bash
* b2c-dx-mcp --toolsets all --debug
* @example mcp.json - Cartridge tools with env vars
* ```json
* {
* "args": ["--toolsets", "CARTRIDGES", "--allow-non-ga-tools"],
* "env": {
* "SFCC_HOSTNAME": "your-sandbox.demandware.net",
* "SFCC_CLIENT_ID": "your-client-id",
* "SFCC_CLIENT_SECRET": "your-client-secret"
* }
* }
* ```
*
* @example mcp.json - Enable debug logging
* ```json
* { "args": ["--toolsets", "all", "--allow-non-ga-tools", "--debug"] }
* ```
*/

import {Flags} from '@oclif/core';
import {BaseCommand} from '@salesforce/b2c-tooling-sdk/cli';
import {DEFAULT_MRT_ORIGIN} from '@salesforce/b2c-tooling-sdk/clients';
import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js';
import {B2CDxMcpServer} from '../server.js';
import {Services} from '../services.js';
Expand All @@ -95,12 +134,26 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
'Salesforce B2C Commerce Cloud Developer Experience MCP Server - Expose B2C Commerce Developer Experience tools to AI assistants';

static examples = [
'<%= config.bin %> <%= command.id %> --toolsets all',
'<%= config.bin %> <%= command.id %> --toolsets STOREFRONTNEXT,MRT',
'<%= config.bin %> <%= command.id %> --tools sfnext_deploy,mrt_bundle_push',
'<%= config.bin %> <%= command.id %> --toolsets STOREFRONTNEXT --tools sfnext_deploy',
'<%= config.bin %> <%= command.id %> --toolsets MRT --config /path/to/dw.json',
'<%= config.bin %> <%= command.id %> --toolsets all --debug',
{
description: 'All toolsets',
command: '<%= config.bin %> --toolsets all --allow-non-ga-tools',
},
{
description: 'Specific toolsets',
command: '<%= config.bin %> --toolsets CARTRIDGES,MRT --allow-non-ga-tools',
},
{
description: 'MRT tools with API key',
command: '<%= config.bin %> --toolsets MRT --mrt-api-key your-api-key --allow-non-ga-tools',
},
{
description: 'Cartridge tools with explicit config',
command: '<%= config.bin %> --toolsets CARTRIDGES --config /path/to/dw.json --allow-non-ga-tools',
},
{
description: 'Debug logging',
command: '<%= config.bin %> --toolsets all --allow-non-ga-tools --debug',
},
];

static flags = {
Expand All @@ -116,6 +169,18 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
parse: async (input) => input.toLowerCase(),
}),

// Auth flags
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note you can get and spread these flags from ...MrtCommand.baseFlags

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clavery That sounds good! For MCP tools, it might need different API keys, do you consider rename api-key to mrt-api-key?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps. although we do want to maximum compatibility with existing conventions (like pwa-kit-dev, etc) which uses --api-key. I'll think about this. Right now we don't have any auth needs that conflict with api-key (all other auths use distinct naming).

One thing that's important is the env variables are the same (which in this case is unambiguous (SFCC_MRT_API_KEY)). So if you don't want to spread from the base command make sure we're keeping those consistent. The idea is that folks can set their configuration once (.env files, dw.json, etc) and ALL of our tools respond in the same way

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clavery I updated to spread from the command and it works well. One thing to note, if config Cursor MCP with .env instead of "env" attribute in mcp.json, .env needs to be under home directory (~) because Cursor runs MCP servers with cwd = home directory.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both .env and env attribute in mcp.json will be supported.

Yeah I know about cursors weird behavior there. Here's what I told Shaurye a few months ago: Once we move this MCP out of PWA kit we'll want to use a more generic env var AND we should support this as an argument as well (i.e. --project ${workspaceFolder}). Again falling back to cwd when not specified (edited)

Basically we should default everything to assuming the current working directory is our project but allow for a --project (or similar) flag to root (or change our directory) to. The ${workspaceFolder} expansion works in cursor

I think this might be a useful flag at the BaseCommand too rather than just being an MCP concern.

All that being said if they don't configure it that way then we don't know where the project is and configuration will just load what it can (And many tools may be useful but others useless)

'mrt-api-key': Flags.string({
description: 'MRT API key for Managed Runtime operations',
env: 'SFCC_MRT_API_KEY',
helpGroup: 'AUTH',
}),
'mrt-cloud-origin': Flags.string({
description: `MRT cloud origin URL for environment-specific ~/.mobify--[hostname] config (default: ${DEFAULT_MRT_ORIGIN})`,
env: 'SFCC_MRT_CLOUD_ORIGIN',
helpGroup: 'AUTH',
}),

// Feature flags
'allow-non-ga-tools': Flags.boolean({
description: 'Enable non-GA (experimental) tools',
Expand All @@ -131,7 +196,7 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
* 1. BaseCommand.init() parses flags and loads config
* 2. Filter and validate toolsets (invalid ones are skipped with warning)
* 3. Create B2CDxMcpServer instance
* 4. Create Services for dependency injection (config, file system access)
* 4. Create Services via Services.create() which resolves MRT auth from flags/env/config
* 5. Register tools based on --toolsets and --tools flags
* 6. Connect to stdio transport (JSON-RPC over stdin/stdout)
* 7. Log startup message to stderr
Expand Down Expand Up @@ -188,10 +253,11 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
},
);

// Create services for dependency injection
// Pass the config path for tools that need to load configuration
const services = new Services({
// Create services with MRT auth resolved from flags/env/config
const services = Services.create({
configPath: this.flags.config,
mrtApiKey: this.flags['mrt-api-key'],
mrtCloudOrigin: this.flags['mrt-cloud-origin'],
});

// Register toolsets
Expand All @@ -202,7 +268,6 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
await server.connect(transport);

// Log startup message using the structured logger
this.logger.info({version: this.config.version, toolsets: TOOLSETS}, 'MCP Server running on stdio');
this.logger.info({enabled: startupFlags.toolsets ?? []}, 'Enabled toolsets');
this.logger.info({version: this.config.version}, 'MCP Server running on stdio');
}
}
Loading
Loading