Skip to content

Commit 1b1e313

Browse files
committed
use BaseCommand class and update dev.js run.js to use import tsx
1 parent 7ebce40 commit 1b1e313

12 files changed

Lines changed: 150 additions & 98 deletions

File tree

packages/b2c-dx-mcp/README.md

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,36 @@ Add to `claude_desktop_config.json`:
9090
// Combine toolsets and tools
9191
"args": ["-y", "@salesforce/b2c-dx-mcp", "--toolsets", "CARTRIDGES", "--tools", "job_run"]
9292

93-
// Explicit dw.json path
94-
"args": ["-y", "@salesforce/b2c-dx-mcp", "--toolsets", "all", "--dw-json", "/path/to/dw.json"]
93+
// Explicit config file path
94+
"args": ["-y", "@salesforce/b2c-dx-mcp", "--toolsets", "all", "--config", "/path/to/dw.json"]
9595

9696
// Enable experimental tools
9797
"args": ["-y", "@salesforce/b2c-dx-mcp", "--toolsets", "all", "--allow-non-ga-tools"]
98+
99+
// Enable debug logging
100+
"args": ["-y", "@salesforce/b2c-dx-mcp", "--toolsets", "all", "--debug"]
98101
```
99102

100103
### Supported Flags
101104

105+
#### MCP-Specific Flags
106+
102107
| Flag | Short | Description |
103108
|------|-------|-------------|
104109
| `--toolsets` | `-s` | Comma-separated toolsets to enable (case-insensitive) |
105110
| `--tools` | `-t` | Comma-separated individual tools to enable (case-insensitive) |
106111
| `--allow-non-ga-tools` | | Enable experimental (non-GA) tools |
107-
| `--dw-json` | | Path to dw.json (optional, auto-discovered) |
112+
113+
#### Global Flags (inherited from SDK)
114+
115+
| Flag | Short | Description |
116+
|------|-------|-------------|
117+
| `--config` | | Path to dw.json config file (auto-discovered if not provided) |
118+
| `--instance` | `-i` | Instance name from configuration file |
119+
| `--log-level` | | Set logging verbosity (trace, debug, info, warn, error, silent) |
120+
| `--debug` | `-D` | Enable debug logging |
121+
| `--json` | | Output logs as JSON lines |
122+
| `--lang` | `-L` | Language for messages |
108123

109124
### Available Toolsets
110125

@@ -216,37 +231,35 @@ npx mcp-inspector --cli node bin/run.js -s all --allow-non-ga-tools \
216231

217232
#### 2. IDE Integration
218233

219-
Configure your IDE to use the local build. First, build the package:
234+
Configure your IDE to use the local server. Choose development mode (no build required) or production mode (requires build).
220235

221-
```bash
222-
pnpm run build
223-
```
236+
**Development Mode** (recommended for active development - uses TypeScript source directly):
224237

225-
**Cursor** (`.cursor/mcp.json`):
226238
```json
227239
{
228240
"mcpServers": {
229241
"b2c-dx-local": {
230-
"command": "node",
231-
"args": ["/full/path/to/packages/b2c-dx-mcp/bin/run.js", "-s", "all"]
242+
"command": "/full/path/to/packages/b2c-dx-mcp/bin/dev.js",
243+
"args": ["-s", "all"]
232244
}
233245
}
234246
}
235247
```
236248

237-
**Claude Desktop** (`claude_desktop_config.json`):
249+
**Production Mode** (uses compiled JavaScript - run `pnpm run build` first):
250+
238251
```json
239252
{
240253
"mcpServers": {
241254
"b2c-dx-local": {
242-
"command": "node",
243-
"args": ["/full/path/to/packages/b2c-dx-mcp/bin/run.js", "-s", "all"]
255+
"command": "/full/path/to/packages/b2c-dx-mcp/bin/run.js",
256+
"args": ["-s", "all"]
244257
}
245258
}
246259
}
247260
```
248261

249-
> **Note:** After making code changes, run `pnpm run build` and restart your IDE to pick up the changes.
262+
> **Note:** For production mode, run `pnpm run build` after code changes and restart your IDE. Development mode picks up changes automatically.
250263
251264
#### 3. JSON-RPC via stdin
252265

packages/b2c-dx-mcp/bin/dev.cmd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@echo off
2+
3+
node --conditions development --import tsx "%~dp0\dev" %*
4+

packages/b2c-dx-mcp/bin/dev.js

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,31 @@
1-
#!/usr/bin/env -S node --loader ts-node/esm --disable-warning=ExperimentalWarning
1+
#!/usr/bin/env -S node --conditions development --import tsx
22
/*
3-
* Copyright 2025, Salesforce, Inc.
4-
*
5-
* Licensed under the Apache License, Version 2.0 (the "License");
6-
* you may not use this file except in compliance with the License.
7-
* You may obtain a copy of the License at
8-
*
9-
* http://www.apache.org/licenses/LICENSE-2.0
10-
*
11-
* Unless required by applicable law or agreed to in writing, software
12-
* distributed under the License is distributed on an "AS IS" BASIS,
13-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14-
* See the License for the specific language governing permissions and
15-
* limitations under the License.
3+
* Copyright (c) 2025, Salesforce, Inc.
4+
* SPDX-License-Identifier: Apache-2
5+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
166
*/
7+
/* global process */
178

189
/**
1910
* Development entry point for MCP server using oclif.
2011
*
2112
* This uses oclif's development mode which:
22-
* - Uses TypeScript source directly (via ts-node/esm loader in shebang)
13+
* - Uses TypeScript source directly (via tsx loader in shebang)
2314
* - Supports the 'development' condition for exports
15+
* - Loads .env file if present for local configuration
2416
* - Provides better error messages and stack traces
2517
*
26-
* Run directly: ./bin/dev.js -s all
27-
* Or with node: node bin/dev.js -s all (uses compiled dist/ files)
18+
* Run directly: ./bin/dev.js mcp -s all
19+
* Or with node: node --conditions development --import tsx bin/dev.js mcp -s all
2820
*/
2921

30-
import { execute } from "@oclif/core";
22+
// Load .env file if present (Node.js native support)
23+
try {
24+
process.loadEnvFile();
25+
} catch {
26+
// .env file not found or not readable, continue without it
27+
}
28+
29+
import {execute} from '@oclif/core';
3130

32-
await execute({ development: true, dir: import.meta.url });
31+
await execute({development: true, dir: import.meta.url});

packages/b2c-dx-mcp/bin/run.cmd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@echo off
2+
3+
node "%~dp0\run" %*
4+

packages/b2c-dx-mcp/bin/run.js

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
#!/usr/bin/env node
22
/*
3-
* Copyright 2025, Salesforce, Inc.
4-
*
5-
* Licensed under the Apache License, Version 2.0 (the "License");
6-
* you may not use this file except in compliance with the License.
7-
* You may obtain a copy of the License at
8-
*
9-
* http://www.apache.org/licenses/LICENSE-2.0
10-
*
11-
* Unless required by applicable law or agreed to in writing, software
12-
* distributed under the License is distributed on an "AS IS" BASIS,
13-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14-
* See the License for the specific language governing permissions and
15-
* limitations under the License.
3+
* Copyright (c) 2025, Salesforce, Inc.
4+
* SPDX-License-Identifier: Apache-2
5+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
166
*/
7+
/* global process */
178

189
/**
1910
* Production entry point for MCP server using oclif.
2011
*
21-
* This uses oclif's single-command strategy to parse arguments
22-
* and start the MCP server with proper flag handling.
12+
* This uses oclif's production mode which:
13+
* - Uses compiled JavaScript from dist/
14+
* - Loads .env file if present for local configuration
15+
*
16+
* Run directly: ./bin/run.js mcp -s all
17+
* Or with node: node bin/run.js mcp -s all
2318
*/
2419

25-
import { execute } from "@oclif/core";
20+
// Load .env file if present (Node.js native support)
21+
try {
22+
process.loadEnvFile();
23+
} catch {
24+
// .env file not found or not readable, continue without it
25+
}
26+
27+
import {execute} from '@oclif/core';
2628

27-
await execute({ dir: import.meta.url });
29+
await execute({dir: import.meta.url});

packages/b2c-dx-mcp/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@
9595
"oclif": "^4",
9696
"prettier": "^3.6.2",
9797
"shx": "^0.3.3",
98-
"ts-node": "^10",
9998
"tsx": "^4",
10099
"typescript": "^5",
101100
"typescript-eslint": "^8"

packages/b2c-dx-mcp/src/commands/mcp.ts

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,27 @@
2323
*
2424
* ## Flags
2525
*
26+
* ### MCP-Specific Flags
2627
* | Flag | Short | Description |
2728
* |------|-------|-------------|
2829
* | `--toolsets` | `-s` | Comma-separated toolsets to enable (case-insensitive) |
2930
* | `--tools` | `-t` | Comma-separated individual tools to enable (case-insensitive) |
3031
* | `--allow-non-ga-tools` | | Enable experimental/non-GA tools |
31-
* | `--dw-json` | | Path to dw.json (optional, auto-discovered if not provided) |
32+
*
33+
* ### Global Flags (inherited from BaseCommand)
34+
* | Flag | Short | Description |
35+
* |------|-------|-------------|
36+
* | `--config` | | Path to dw.json config file (auto-discovered if not provided) |
37+
* | `--instance` | `-i` | Instance name from configuration file |
38+
* | `--log-level` | | Set logging verbosity (trace, debug, info, warn, error, silent) |
39+
* | `--debug` | `-D` | Enable debug logging |
40+
* | `--json` | | Output logs as JSON lines |
41+
* | `--lang` | `-L` | Language for messages |
3242
*
3343
* ## Configuration Priority
3444
*
3545
* 1. Environment variables (SFCC_*) - highest priority, override dw.json
36-
* 2. dw.json file (explicit path via --dw-json, or auto-discovered)
46+
* 2. dw.json file (explicit path via --config, or auto-discovered)
3747
* 3. Auto-discovery (searches upward from cwd)
3848
*
3949
* ## Toolset Validation
@@ -61,13 +71,19 @@
6171
* b2c-dx-mcp -s SCAPI -t cartridge_deploy
6272
* ```
6373
*
64-
* @example Specify dw.json location
74+
* @example Specify config file location
75+
* ```bash
76+
* b2c-dx-mcp -s all --config /path/to/dw.json
77+
* ```
78+
*
79+
* @example Enable debug logging
6580
* ```bash
66-
* b2c-dx-mcp -s all --dw-json /path/to/dw.json
81+
* b2c-dx-mcp -s all -D
6782
* ```
6883
*/
6984

70-
import { Command, Flags } from "@oclif/core";
85+
import { Flags } from "@oclif/core";
86+
import { BaseCommand } from "@salesforce/b2c-tooling-sdk/cli";
7187
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7288
import { B2CDxMcpServer } from "../server.js";
7389
import { Services } from "../services.js";
@@ -78,12 +94,15 @@ import { TOOLSETS, type StartupFlags } from "../utils/index.js";
7894
* oclif Command that starts the B2C DX MCP server.
7995
*
8096
* Uses oclif's single-command strategy - this IS the CLI, not a subcommand.
81-
* Inherits from oclif's Command class which provides:
97+
* Extends BaseCommand from @salesforce/b2c-tooling-sdk which provides:
98+
* - Global flags for config, logging, and debugging
99+
* - Structured pino logging via `this.logger`
100+
* - Automatic dw.json loading via `this.resolvedConfig`
82101
* - `this.config` - package.json metadata and standard config paths
83-
* - `this.parse()` - type-safe flag parsing
84-
* - `this.error()` - formatted error output
85102
*/
86-
export default class McpServerCommand extends Command {
103+
export default class McpServerCommand extends BaseCommand<
104+
typeof McpServerCommand
105+
> {
87106
static description =
88107
"Salesforce B2C Commerce Cloud Developer Experience MCP Server - Expose B2C Commerce Developer Experience tools to AI assistants";
89108

@@ -92,7 +111,8 @@ export default class McpServerCommand extends Command {
92111
"<%= config.bin %> <%= command.id %> --toolsets STOREFRONTNEXT,MRT",
93112
"<%= config.bin %> <%= command.id %> --tools sfnext_deploy,mrt_bundle_push",
94113
"<%= config.bin %> <%= command.id %> --toolsets STOREFRONTNEXT --tools sfnext_deploy",
95-
"<%= config.bin %> <%= command.id %> --toolsets MRT --dw-json /path/to/dw.json",
114+
"<%= config.bin %> <%= command.id %> --toolsets MRT --config /path/to/dw.json",
115+
"<%= config.bin %> <%= command.id %> --toolsets all --debug",
96116
];
97117

98118
static flags = {
@@ -116,20 +136,13 @@ export default class McpServerCommand extends Command {
116136
env: "SFCC_ALLOW_NON_GA_TOOLS",
117137
default: false,
118138
}),
119-
120-
// Configuration
121-
"dw-json": Flags.string({
122-
description:
123-
"Path to dw.json (optional, auto-discovered if not provided)",
124-
env: "SFCC_DW_JSON",
125-
}),
126139
};
127140

128141
/**
129142
* Main entry point - starts the MCP server.
130143
*
131144
* Execution flow:
132-
* 1. Parse flags using oclif (with case normalization)
145+
* 1. BaseCommand.init() parses flags and loads config
133146
* 2. Filter and validate toolsets (invalid ones are skipped with warning)
134147
* 3. Create B2CDxMcpServer instance
135148
* 4. Create Services for dependency injection (config, file system access)
@@ -140,26 +153,30 @@ export default class McpServerCommand extends Command {
140153
* @throws Never throws - invalid toolsets are filtered, not rejected
141154
*
142155
* @remarks
156+
* BaseCommand provides:
157+
* - `this.flags` - Parsed flags including global flags (config, debug, log-level, etc.)
158+
* - `this.resolvedConfig` - Loaded dw.json configuration
159+
* - `this.logger` - Structured pino logger
160+
*
143161
* oclif provides standard config paths via `this.config`:
144162
* - `this.config.configDir` - User config (~/.config/b2c-dx-mcp)
145163
* - `this.config.dataDir` - User data (~/.local/share/b2c-dx-mcp)
146164
* - `this.config.cacheDir` - Cache (~/.cache/b2c-dx-mcp)
147165
* These can be exposed to Services if needed for features like telemetry or caching.
148166
*/
149167
async run(): Promise<void> {
150-
const { flags } = await this.parse(McpServerCommand);
151-
168+
// Flags are already parsed by BaseCommand.init()
152169
// Parse toolsets and tools from comma-separated strings
153170
// Note: toolsets are uppercased, tools are lowercased by their parse functions
154171
const startupFlags: StartupFlags = {
155-
toolsets: flags.toolsets
156-
? flags.toolsets.split(",").map((s) => s.trim())
172+
toolsets: this.flags.toolsets
173+
? this.flags.toolsets.split(",").map((s) => s.trim())
157174
: undefined,
158-
tools: flags.tools
159-
? flags.tools.split(",").map((s) => s.trim())
175+
tools: this.flags.tools
176+
? this.flags.tools.split(",").map((s) => s.trim())
160177
: undefined,
161-
allowNonGaTools: flags["allow-non-ga-tools"],
162-
dwJsonPath: flags["dw-json"],
178+
allowNonGaTools: this.flags["allow-non-ga-tools"],
179+
configPath: this.flags.config,
163180
};
164181

165182
// TODO: Telemetry - Initialize telemetry unless disabled
@@ -191,8 +208,9 @@ export default class McpServerCommand extends Command {
191208
);
192209

193210
// Create services for dependency injection
211+
// Pass the config path for tools that need to load configuration
194212
const services = new Services({
195-
dwJsonPath: startupFlags.dwJsonPath,
213+
configPath: this.flags.config,
196214
});
197215

198216
// Register toolsets
@@ -202,11 +220,14 @@ export default class McpServerCommand extends Command {
202220
const transport = new StdioServerTransport();
203221
await server.connect(transport);
204222

205-
// Log startup message to stderr (not stdout, which is for MCP protocol)
206-
console.error(`✅ MCP Server v${this.config.version} running on stdio`);
207-
console.error(` Available toolsets: ${TOOLSETS.join(", ")}`);
208-
console.error(
209-
` Enabled: ${(startupFlags.toolsets ?? []).join(", ") || "(none specified)"}`,
223+
// Log startup message using the structured logger
224+
this.logger.info(
225+
{ version: this.config.version, toolsets: TOOLSETS },
226+
"MCP Server running on stdio",
227+
);
228+
this.logger.info(
229+
{ enabled: startupFlags.toolsets ?? [] },
230+
"Enabled toolsets",
210231
);
211232
}
212233
}

0 commit comments

Comments
 (0)