Skip to content

Commit 1ef9b9e

Browse files
committed
Remove no telemetry flag, add unit tests
1 parent d1c64b3 commit 1ef9b9e

6 files changed

Lines changed: 350 additions & 8 deletions

File tree

packages/b2c-dx-mcp/README.md

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,17 @@ The server automatically detects your project type and enables relevant tools. S
1616

1717
The most important flag is **`--working-directory`** (or env var `SFCC_WORKING_DIRECTORY`). It tells the server where your project is located, enabling:
1818

19+
<<<<<<< HEAD
1920
1. **Auto-discovery** - Detects your project type and enables appropriate toolsets
2021
2. **Configuration loading** - Reads [`dw.json`](https://salesforcecommercecloud.github.io/b2c-developer-tooling/guide/configuration.html#configuration-file) from your project for credentials
2122
3. **Scaffolding** - Creates new files in the correct location
23+
=======
24+
| Flag | Env Variable | Description |
25+
|------|--------------|-------------|
26+
| `--toolsets` | `SFCC_TOOLSETS` | Comma-separated toolsets to enable (case-insensitive) |
27+
| `--tools` | `SFCC_TOOLS` | Comma-separated individual tools to enable (case-insensitive) |
28+
| `--allow-non-ga-tools` | `SFCC_ALLOW_NON_GA_TOOLS` | Enable experimental (non-GA) tools |
29+
>>>>>>> 1ca55c6 (Remove no telemetry flag, add unit tests)
2230
2331
> **Important:** MCP clients like Cursor and Claude Desktop spawn servers from the home directory (`~`), not your project. Always set `--working-directory`.
2432
@@ -327,6 +335,227 @@ echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node bin/dev.js --toolse
327335
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"cartridge_deploy","arguments":{}}}' | node bin/dev.js --toolsets all --allow-non-ga-tools
328336
```
329337

338+
### Configuration
339+
340+
> **Note:** Configuration is not currently required as all tools are placeholder implementations. This section will be relevant once tools are fully implemented.
341+
342+
Different tools require different types of configuration:
343+
344+
| Tool Type | Configuration Required |
345+
|-----------|----------------------|
346+
| **MRT tools** (e.g., `mrt_bundle_push`) | API key + project |
347+
| **B2C instance tools** (e.g., `cartridge_deploy`) | dw.json or instance flags |
348+
| **Local tools** (e.g., scaffolding) | None |
349+
350+
#### MRT Configuration
351+
352+
MRT tools require an **API key** and **project**. The **environment** is optional (for deployments).
353+
354+
| Setting | Flag | Env Variable | Fallback |
355+
|---------|------|--------------|----------|
356+
| API key | `--api-key` | `SFCC_MRT_API_KEY` | `~/.mobify` |
357+
| Project | `--project` | `SFCC_MRT_PROJECT` ||
358+
| Environment | `--environment` | `SFCC_MRT_ENVIRONMENT` ||
359+
360+
> Priority: Flag > Env variable > `~/.mobify` file
361+
362+
**Example:**
363+
364+
```json
365+
{
366+
"mcpServers": {
367+
"b2c-dx": {
368+
"command": "/path/to/packages/b2c-dx-mcp/bin/dev.js",
369+
"args": [
370+
"--toolsets", "MRT",
371+
"--project", "my-project",
372+
"--environment", "staging",
373+
"--api-key", "your-api-key"
374+
]
375+
}
376+
}
377+
}
378+
```
379+
380+
Or use environment variables instead of flags:
381+
382+
```json
383+
{
384+
"mcpServers": {
385+
"b2c-dx": {
386+
"command": "/path/to/packages/b2c-dx-mcp/bin/dev.js",
387+
"args": ["--toolsets", "MRT"],
388+
"env": {
389+
"SFCC_MRT_API_KEY": "your-api-key",
390+
"SFCC_MRT_PROJECT": "my-project",
391+
"SFCC_MRT_ENVIRONMENT": "staging"
392+
}
393+
}
394+
}
395+
}
396+
```
397+
398+
> **Note:** Make sure the script is executable: `chmod +x /path/to/packages/b2c-dx-mcp/bin/dev.js`
399+
400+
#### Environment-Specific Config
401+
402+
If you have a `~/.mobify` file from the `b2c` CLI, the MCP server will use it as a fallback for API key:
403+
404+
```json
405+
{
406+
"api_key": "your-api-key"
407+
}
408+
```
409+
410+
For non-production environments, use `--cloud-origin` to select an environment-specific config file:
411+
412+
| `--cloud-origin` | Config File |
413+
|------------------|-------------|
414+
| (not set) | `~/.mobify` |
415+
| `https://cloud-staging.mobify.com` | `~/.mobify--cloud-staging.mobify.com` |
416+
| `https://cloud-dev.mobify.com` | `~/.mobify--cloud-dev.mobify.com` |
417+
418+
#### B2C Instance Config (dw.json)
419+
420+
Tools that interact with B2C Commerce instances (e.g., `cartridge_deploy`, SCAPI tools) require instance credentials.
421+
422+
**Authentication Methods:**
423+
424+
| Method | Credentials | Used By |
425+
|--------|-------------|---------|
426+
| **Basic auth** | `--username` + `--password` | WebDAV tools (`cartridge_deploy`) |
427+
| **OAuth** | `--client-id` + `--client-secret` | OCAPI tools, SCAPI tools |
428+
429+
> **Recommendation:** Use Basic auth (username/password) for WebDAV tools like `cartridge_deploy`. OAuth credentials (client-id/client-secret) are required for OCAPI/SCAPI tools. If you need both WebDAV and OCAPI tools, configure all four credentials.
430+
431+
**Priority order** (highest to lowest):
432+
433+
1. Flags (`--server`, `--username`, `--password`, `--client-id`, `--client-secret`)
434+
2. Environment variables (`SFCC_*`)
435+
3. `dw.json` file (via `--config` flag or auto-discovery)
436+
437+
**Option A: Flags with Basic auth (for WebDAV tools like cartridge_deploy)**
438+
439+
```json
440+
{
441+
"mcpServers": {
442+
"b2c-dx": {
443+
"command": "/path/to/packages/b2c-dx-mcp/bin/dev.js",
444+
"args": [
445+
"--toolsets", "CARTRIDGES",
446+
"--server", "your-sandbox.demandware.net",
447+
"--username", "your.username",
448+
"--password", "your-access-key"
449+
]
450+
}
451+
}
452+
}
453+
```
454+
455+
**Option B: Flags with OAuth (for OCAPI/SCAPI tools, or WebDAV fallback)**
456+
457+
```json
458+
{
459+
"mcpServers": {
460+
"b2c-dx": {
461+
"command": "/path/to/packages/b2c-dx-mcp/bin/dev.js",
462+
"args": [
463+
"--toolsets", "SCAPI",
464+
"--server", "your-sandbox.demandware.net",
465+
"--client-id", "your-client-id",
466+
"--client-secret", "your-client-secret"
467+
]
468+
}
469+
}
470+
}
471+
```
472+
473+
**Option C: Environment variables (all credentials)**
474+
475+
```json
476+
{
477+
"mcpServers": {
478+
"b2c-dx": {
479+
"command": "/path/to/packages/b2c-dx-mcp/bin/dev.js",
480+
"args": ["--toolsets", "CARTRIDGES"],
481+
"env": {
482+
"SFCC_SERVER": "your-sandbox.demandware.net",
483+
"SFCC_USERNAME": "your.username",
484+
"SFCC_PASSWORD": "your-access-key",
485+
"SFCC_CLIENT_ID": "your-client-id",
486+
"SFCC_CLIENT_SECRET": "your-client-secret",
487+
"SFCC_CODE_VERSION": "version1"
488+
}
489+
}
490+
}
491+
}
492+
```
493+
494+
**Option D: dw.json with explicit path**
495+
496+
```json
497+
{
498+
"mcpServers": {
499+
"b2c-dx": {
500+
"command": "/path/to/packages/b2c-dx-mcp/bin/dev.js",
501+
"args": ["--toolsets", "CARTRIDGES", "--config", "/path/to/dw.json"]
502+
}
503+
}
504+
}
505+
```
506+
507+
**Option E: dw.json with auto-discovery**
508+
509+
When `--config` is not provided, the MCP server searches upward from `~/` for a `dw.json` file.
510+
511+
> **Note:** Auto-discovery starts from the home directory, so it won't find project-level `dw.json` files. Use `--config` with an explicit path instead.
512+
513+
```json
514+
{
515+
"hostname": "your-sandbox.demandware.net",
516+
"username": "your.username",
517+
"password": "your-access-key",
518+
"client-id": "your-client-id",
519+
"client-secret": "your-client-secret",
520+
"code-version": "version1"
521+
}
522+
```
523+
524+
> **Note:** Flags override environment variables, and environment variables override `dw.json`. You can mix sources (e.g., secrets via env vars, other settings via dw.json).
525+
526+
## Telemetry
527+
528+
The MCP server collects anonymous usage telemetry to help improve the developer experience. Telemetry is enabled by default and can be disabled by setting the `SFCC_TELEMETRY` environment variable to `false`.
529+
530+
### What We Collect
531+
532+
- **Server lifecycle events**: When the server starts, stops, or encounters errors
533+
- **Tool usage**: Which tools are called and their execution time (not the arguments or results)
534+
- **Environment info**: Platform, architecture, Node.js version, and package version
535+
536+
### What We Don't Collect
537+
538+
- **No credentials**: No API keys, passwords, or secrets
539+
- **No business data**: No product data, customer information, or site content
540+
- **No tool arguments**: No input parameters or output results from tool calls
541+
- **No file contents**: No source code, configuration files, or project data
542+
543+
### Disabling Telemetry
544+
545+
Set the `SFCC_TELEMETRY` environment variable to `false`:
546+
547+
```json
548+
{
549+
"mcpServers": {
550+
"b2c-dx": {
551+
"command": "/path/to/packages/b2c-dx-mcp/bin/dev.js",
552+
"args": ["--toolsets", "all"],
553+
"env": { "SFCC_TELEMETRY": "false" }
554+
}
555+
}
556+
}
557+
```
558+
330559
## License
331560

332561
Apache-2.0

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
* | `--toolsets` | `SFCC_TOOLSETS` | Comma-separated toolsets to enable (case-insensitive) |
2020
* | `--tools` | `SFCC_TOOLS` | Comma-separated individual tools to enable (case-insensitive) |
2121
* | `--allow-non-ga-tools` | `SFCC_ALLOW_NON_GA_TOOLS` | Enable experimental/non-GA tools |
22-
* | `--no-telemetry` | `SFCC_NO_TELEMETRY` | Disable telemetry collection |
22+
*
23+
* ### Environment Variables (no flag equivalent)
24+
* | Env Variable | Description |
25+
* |--------------|-------------|
26+
* | `SFCC_TELEMETRY` | Set to `false` to disable telemetry collection |
2327
*
2428
* ### MRT Flags (from MrtCommand.baseFlags)
2529
* | Flag | Env Variable | Description |
@@ -211,11 +215,6 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
211215
env: 'SFCC_ALLOW_NON_GA_TOOLS',
212216
default: false,
213217
}),
214-
'no-telemetry': Flags.boolean({
215-
description: 'Disable telemetry collection',
216-
env: 'SFCC_NO_TELEMETRY',
217-
default: false,
218-
}),
219218
};
220219

221220
/**
@@ -288,9 +287,9 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
288287
workingDirectory: this.flags['working-directory'],
289288
};
290289

291-
// Initialize telemetry unless disabled
290+
// Initialize telemetry unless disabled via SFCC_TELEMETRY=false
292291
let telemetry: Telemetry | undefined;
293-
if (!this.flags['no-telemetry']) {
292+
if (process.env.SFCC_TELEMETRY !== 'false') {
294293
telemetry = createTelemetry({
295294
project: 'b2c-dx-mcp',
296295
appInsightsKey: loadAppInsightsKey(),

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ describe('McpServerCommand', () => {
3535
expect(flag.default).to.equal(false);
3636
});
3737

38+
it('should not have a no-telemetry flag (telemetry controlled via SFCC_TELEMETRY env var only)', () => {
39+
// Telemetry is disabled via SFCC_TELEMETRY=false env var, not a CLI flag
40+
// This keeps the CLI cleaner and prevents accidental disabling
41+
const flags = McpServerCommand.flags as Record<string, unknown>;
42+
expect(flags['no-telemetry']).to.be.undefined;
43+
});
44+
3845
it('should inherit config flag from BaseCommand', () => {
3946
// config flag is inherited from BaseCommand.baseFlags
4047
const flag = McpServerCommand.baseFlags.config;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2025, Salesforce, Inc.
3+
* SPDX-License-Identifier: Apache-2
4+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
7+
import {expect} from 'chai';
8+
import {APPLICATION_INSIGHTS_CONNECTION_STRING} from '../src/config.js';
9+
10+
describe('config', () => {
11+
describe('APPLICATION_INSIGHTS_CONNECTION_STRING', () => {
12+
it('should export a non-empty connection string', () => {
13+
expect(APPLICATION_INSIGHTS_CONNECTION_STRING).to.be.a('string');
14+
expect(APPLICATION_INSIGHTS_CONNECTION_STRING.length).to.be.greaterThan(0);
15+
});
16+
17+
it('should contain InstrumentationKey', () => {
18+
expect(APPLICATION_INSIGHTS_CONNECTION_STRING).to.include('InstrumentationKey=');
19+
});
20+
21+
it('should contain IngestionEndpoint', () => {
22+
expect(APPLICATION_INSIGHTS_CONNECTION_STRING).to.include('IngestionEndpoint=');
23+
});
24+
});
25+
});

packages/b2c-dx-mcp/test/server.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,33 @@
77
import {expect} from 'chai';
88
import {z} from 'zod';
99
import {B2CDxMcpServer} from '../src/server.js';
10+
import type {Telemetry} from '@salesforce/b2c-tooling-sdk/telemetry';
11+
12+
/**
13+
* Mock telemetry for testing.
14+
*/
15+
class MockTelemetry {
16+
public attributes: Record<string, unknown> = {};
17+
public events: Array<{name: string; attributes: Record<string, unknown>}> = [];
18+
public started = false;
19+
public stopped = false;
20+
21+
addAttributes(attrs: Record<string, unknown>): void {
22+
this.attributes = {...this.attributes, ...attrs};
23+
}
24+
25+
sendEvent(name: string, attributes: Record<string, unknown> = {}): void {
26+
this.events.push({name, attributes});
27+
}
28+
29+
async start(): Promise<void> {
30+
this.started = true;
31+
}
32+
33+
stop(): void {
34+
this.stopped = true;
35+
}
36+
}
1037

1138
// Handlers extracted to module scope to reduce callback nesting depth
1239
const simpleHandler = async () => ({content: [{type: 'text' as const, text: 'ok'}]});
@@ -79,4 +106,30 @@ describe('B2CDxMcpServer', () => {
79106
expect(server.isConnected()).to.be.false;
80107
});
81108
});
109+
110+
describe('telemetry integration', () => {
111+
it('should accept telemetry in options', () => {
112+
const mockTelemetry = new MockTelemetry();
113+
114+
const server = new B2CDxMcpServer(
115+
{name: 'test-server', version: '1.0.0'},
116+
{telemetry: mockTelemetry as unknown as Telemetry},
117+
);
118+
119+
expect(server).to.be.instanceOf(B2CDxMcpServer);
120+
});
121+
122+
it('should work without telemetry (telemetry disabled)', () => {
123+
// When telemetry is undefined, server should still work
124+
const server = new B2CDxMcpServer({name: 'test-server', version: '1.0.0'}, {telemetry: undefined});
125+
126+
expect(server).to.be.instanceOf(B2CDxMcpServer);
127+
});
128+
129+
it('should create server without options (no telemetry)', () => {
130+
const server = new B2CDxMcpServer({name: 'test-server', version: '1.0.0'});
131+
132+
expect(server).to.be.instanceOf(B2CDxMcpServer);
133+
});
134+
});
82135
});

0 commit comments

Comments
 (0)