Install AI Tools

B2C Commerce tools, documentation, and skills for your assistant.

Claude

Install the plugin Recommended

bash
claude plugin marketplace add SalesforceCommerceCloud/b2c-developer-tooling
claude plugin install b2c-dx-mcp@b2c-developer-tooling

Start a new Claude Code session. To install for the current project only, run it from your project directory with --scope project.

Manual MCP setup
bash
claude mcp add --transport stdio --scope user b2c-dx-mcp -- npx -y @salesforce/b2c-dx-mcp@latest

Start a new session. To configure the current project only, run it from your project directory with --scope project. See Claude Code MCP setup.

Claude Desktop setup

Codex

Install the plugin Recommended

bash
codex plugin marketplace add SalesforceCommerceCloud/b2c-developer-tooling
codex plugin add b2c-dx-mcp@b2c-developer-tooling

Start a new Codex session in your project. This setup also works with the Codex IDE extension and the ChatGPT Work desktop app.

Manual MCP setup
bash
codex mcp add b2c-dx-mcp -- npx -y @salesforce/b2c-dx-mcp@latest

Or add this to ~/.codex/config.toml (or $CODEX_HOME/config.toml if customized):

toml
[mcp_servers.b2c-dx-mcp]
command = "npx"
args = ["-y", "@salesforce/b2c-dx-mcp@latest"]

Start a new session. See Codex MCP configuration.

ChatGPT online setup

VS Code

Install the plugin Recommended

  1. Open the Command Palette (Cmd/Ctrl+Shift+P) and run Chat: Install Plugin from Source.
  2. Enter SalesforceCommerceCloud/b2c-developer-tooling.
  3. Select b2c-dx-mcp and follow the installation prompts.
  4. Start a new chat in GitHub Copilot.
Manual MCP setup

Add this to .vscode/mcp.json in your workspace:

json
{
  "servers": {
    "b2c-dx-mcp": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@salesforce/b2c-dx-mcp@latest"]
    }
  }
}

See VS Code MCP setup.

Copilot CLI setup

Cursor

Reload the MCP server in Cursor after installation.

Manual MCP setup

Add this to .cursor/mcp.json in your project:

json
{
  "mcpServers": {
    "b2c-dx-mcp": {
      "command": "npx",
      "args": ["-y", "@salesforce/b2c-dx-mcp@latest"]
    }
  }
}

For all projects, use ~/.cursor/mcp.json instead.

See Cursor's MCP documentation.

OpenCode

Add this to opencode.json in your project:

json
{
  "mcp": {
    "b2c-dx-mcp": {
      "type": "local",
      "command": ["npx", "-y", "@salesforce/b2c-dx-mcp@latest"],
      "enabled": true
    }
  }
}

Restart OpenCode. For all projects, use ~/.config/opencode/opencode.json. See OpenCode MCP setup.

Gemini

Run:

bash
gemini mcp add --scope user b2c-dx-mcp -- npx -y @salesforce/b2c-dx-mcp@latest

Start a new Gemini CLI session. To configure the current project only, run it from your project directory with --scope project. See Gemini CLI MCP setup.

No separate skills plugins needed.

Other clients and manual setup →
Skip to content
View as Markdown
View as Markdown

b2c_tooling_sdk.operations.logs ​

Log operations for B2C Commerce instances.

Mirrors src/operations/logs/index.ts. Provides functions for listing, tailing, and analyzing log files on B2C Commerce instances via WebDAV.

Example - list log files:

python
from b2c_tooling_sdk.operations.logs import list_log_files, ListLogsOptions

files = await list_log_files(
    instance, ListLogsOptions(prefixes=["error", "customerror"], sort_by="date", sort_order="desc")
)

for file in files:
    print(f"{file.name} ({file.size} bytes)")

Example - tail logs in real-time:

python
from b2c_tooling_sdk.operations.logs import (
    tail_logs, TailLogsOptions, create_path_normalizer, PathNormalizerOptions,
)

normalizer = create_path_normalizer(PathNormalizerOptions(cartridge_path="./cartridges"))

result = await tail_logs(
    instance,
    TailLogsOptions(
        prefixes=["error", "customerror"],
        path_normalizer=normalizer,
        on_entry=lambda entry: print(f"[{entry.file}] {entry.level}: {entry.message}"),
        on_error=lambda err: print(f"Error: {err}"),
    ),
)

# Stop after 30 seconds.
await asyncio.sleep(30)
await result.stop()
await result.done

Example - get recent logs (one-shot):

python
from b2c_tooling_sdk.operations.logs import get_recent_logs, GetRecentLogsOptions

entries = await get_recent_logs(instance, GetRecentLogsOptions(prefixes=["error"], max_entries=50))

for entry in entries:
    print(f"[{entry.timestamp}] {entry.message}")

Classes ​

GetRecentLogsOptions ​

python
class GetRecentLogsOptions

Options for getting recent logs (one-shot retrieval).

Fields

NameTypeDefault
prefixeslist[str]field(default_factory=lambda: ['error', 'customerror'])
max_entriesint100
tail_bytesint65536
path_normalizerCallable[[str], str] | NoneNone

ListLogsOptions ​

python
class ListLogsOptions

Options for listing log files.

Fields

NameTypeDefault
prefixeslist[str] | NoneNone
sort_byLiteral['name', 'date', 'size']'date'
sort_orderLiteral['asc', 'desc']'desc'

LogEntry ​

python
class LogEntry

Represents a parsed log entry.

Fields

NameTypeDefault
filestr
messagestr
rawstr
levelstr | NoneNone
timestampstr | NoneNone

LogFile ​

python
class LogFile

Represents a log file on a B2C Commerce instance.

Fields

NameType
namestr
prefixstr
sizeint
last_modifieddatetime
pathstr

PathNormalizerOptions ​

python
class PathNormalizerOptions

Options for creating a path normalizer.

Fields

NameTypeDefault
cartridge_pathstr | NoneNone
cartridgeslist[CartridgeMapping]field(default_factory=list)

TailLogsCallbacks ​

python
class TailLogsCallbacks

Callback functions for tail operation events.

Fields

NameTypeDefault
on_entryCallable[[LogEntry], None] | NoneNone
on_errorCallable[[Exception], None] | NoneNone
on_file_discoveredCallable[[LogFile], None] | NoneNone
on_file_rotatedCallable[[LogFile], None] | NoneNone

TailLogsOptions ​

python
class TailLogsOptions(TailLogsCallbacks)

Options for tailing logs.

Fields

NameTypeDefault
prefixeslist[str]field(default_factory=lambda: ['error', 'customerror'])
poll_intervalfloat3.0
last_entriesint1
max_entriesint | NoneNone
path_normalizerCallable[[str], str] | NoneNone

TailLogsResult ​

python
class TailLogsResult

Result of a tail operation.

Fields

NameType
stopCallable[[], Awaitable[None]]
fileslist[LogFile]
entrieslist[LogEntry]
doneAwaitable[None]

Functions ​

aggregate_log_entries ​

python
def aggregate_log_entries(lines: list[str], pending_lines: list[str] | None = None) -> AggregatedLogEntries

Aggregate lines into multi-line log entries.

B2C log entries can span multiple lines. A new entry starts when a line begins with a timestamp pattern: [YYYY-MM-DD HH:MM:SS.mmm GMT].

Parameters

NameTypeDescription
lineslist[str]Individual lines.
pending_lineslist[str] | NoneLines carried over from the previous chunk (incomplete entry).

Returns: Complete entries and any pending lines for the next chunk.

create_path_normalizer ​

python
def create_path_normalizer(options: PathNormalizerOptions) -> Callable[[str], str] | None

Create a path normalizer function for converting remote cartridge paths to local paths in log messages.

Supports two modes:

  1. Cartridge mappings (precise): Uses discovered cartridges to map each cartridge name to its actual local path. Best for projects with cartridges in different locations.
  2. Cartridge path (simple): Prefixes all paths with a base directory. Best when all cartridges are in a single directory.

Parameters

NameTypeDescription
optionsPathNormalizerOptionsNormalizer options.

Returns: Function that normalizes paths in a message string, or None if no options were provided. >>> cartridges = find_cartridges('./my-project') >>> normalize = create_path_normalizer(PathNormalizerOptions(cartridges=cartridges)) >>> # Or, using a simple cartridge path: >>> normalize = create_path_normalizer(PathNormalizerOptions(cartridge_path='./cartridges')) >>> # Input: "(app_storefront/cartridge/controllers/Home.js:45)" >>> # Output: "(./cartridges/app_storefront/cartridge/controllers/Home.js:45)"

discover_and_create_normalizer ​

python
def discover_and_create_normalizer(directory: str | None = None) -> Callable[[str], str] | None

Discover cartridges and create a path normalizer automatically.

Convenience function that combines find_cartridges with create_path_normalizer for easy setup. Cartridge paths are converted to relative paths from the current project directory.

Parameters

NameTypeDescription
directorystr | NoneDirectory to search for cartridges (defaults to cwd).

Returns: Path normalizer function, or None if no cartridges were found. >>> normalize = discover_and_create_normalizer() >>> normalize = discover_and_create_normalizer('./my-project')

extract_paths ​

python
def extract_paths(message: str) -> list[str]

Extract all cartridge paths from a message.

Useful for testing or analysis of log messages.

Parameters

NameTypeDescription
messagestrLog message to extract paths from.

Returns: List of extracted paths. >>> extract_paths("Error at (app_storefront/cartridge/controllers/Home.js:45)") ['app_storefront/cartridge/controllers/Home.js:45']

extract_prefix ​

python
def extract_prefix(filename: str) -> str

Extract the log prefix from a filename.

Parameters

NameTypeDescription
filenamestrLog file name (e.g., "error-blade1-20250125.log").

Returns: The prefix (e.g., "error") or "unknown". >>> extract_prefix("error-blade1-20250125.log") 'error' >>> extract_prefix("customerror-blade1-20250125.log") 'customerror' >>> extract_prefix("custom-mylog-blade1-20250125.log") 'custom-mylog'

filter_by_level ​

python
def filter_by_level(entries: list[LogEntry], levels: list[str]) -> list[LogEntry]

Filter entries by log level.

python
def filter_by_search(entries: list[LogEntry], search: str) -> list[LogEntry]

Filter entries by text search (case-insensitive substring match).

filter_by_since ​

python
def filter_by_since(entries: list[LogEntry], since: datetime) -> list[LogEntry]

Filter entries by timestamp.

get_recent_logs ​

python
async def get_recent_logs(instance: B2CInstance, options: GetRecentLogsOptions | None = None) -> list[LogEntry]

Get recent log entries (one-shot retrieval).

Useful for MCP server integration or programmatic access without continuous tailing. Reads the tail end of log files and returns parsed entries.

Parameters

NameTypeDescription
instanceB2CInstanceB2C instance to get logs from.
optionsGetRecentLogsOptions | NoneRetrieval options.

Returns: Recent log entries.

list_log_files ​

python
async def list_log_files(instance: B2CInstance, options: ListLogsOptions | None = None) -> list[LogFile]

List log files on a B2C Commerce instance.

Filters in ListLogsOptions.prefixes are matched in one of two ways:

  • Prefix filters (no /, e.g. "error") match the extracted log-category prefix of files in the top-level Logs/ directory.
  • Path filters (contain a /, e.g. "internal/server") recurse into the named subdirectory of Logs/ and match against each file's path relative to Logs/. This is the only case that lists subdirectories — by default only the top-level Logs/ directory is scanned.

Parameters

NameTypeDescription
instanceB2CInstanceB2C instance to list logs from.
optionsListLogsOptions | NoneListing options (filters, sorting).

Returns: List of log files.

matches_level ​

python
def matches_level(entry: LogEntry, levels: list[str]) -> bool

Check if a single entry matches the specified log levels.

Used for streaming/tail scenarios where entries are filtered one at a time.

python
def matches_search(entry: LogEntry, search: str) -> bool

Check if a single entry matches the search text (case-insensitive).

Used for streaming/tail scenarios where entries are filtered one at a time.

parse_log_entry ​

python
def parse_log_entry(first_line: str, file: str, full_message: str, path_normalizer: Callable[[str], str] | None = None) -> LogEntry

Parse the first line of a log entry to extract timestamp, level, and message.

Expected format: [timestamp GMT] LEVEL context - message. Example: [2025-01-25 10:30:45.123 GMT] ERROR PipelineCallServlet|... - Error message.

The message field will contain:

  • The content portion from the first line (after LEVEL)
  • Plus any continuation lines (stack traces, etc.)

If the standard B2C log format is not matched, returns an unparsed entry with only the file, message, and raw fields. The timestamp and level fields will be None in this case, but the raw log line is preserved for debugging or recovery purposes.

Parameters

NameTypeDescription
first_linestrFirst line of the log entry.
filestrFile name the entry came from.
full_messagestrComplete raw message including all lines.
path_normalizerCallable[[str], str] | NoneOptional function to normalize paths in the message.

parse_log_timestamp ​

python
def parse_log_timestamp(timestamp: str) -> datetime | None

Parse a B2C log timestamp into a datetime.

Expected format: "2025-01-25 10:30:45.123 GMT".

parse_relative_time ​

python
def parse_relative_time(time_str: str) -> int | None

Parse a relative time string (e.g., "5m", "1h", "2d") into milliseconds.

Returns: The duration in milliseconds, or None if time_str is not a valid relative time format.

parse_since_time ​

python
def parse_since_time(since_str: str, now: datetime | None = None) -> datetime

Parse a since value into a datetime.

Supports:

  • Relative times: "5m", "1h", "2d"
  • ISO 8601: "2026-01-25T10:00:00"

Parameters

NameTypeDescription
since_strstrThe value to parse.
nowdatetime | NoneReference time for relative values (defaults to the current, timezone-aware UTC time). Injectable so callers that resolve several bounds together, or that need deterministic behavior in tests, can pin a single "now". If a naive (tzinfo-less) value is passed, it is used as-is for relative-time arithmetic.

Raises

  • ValueError — if since_str is neither a valid relative time nor a valid ISO 8601 timestamp.

split_lines ​

python
def split_lines(content: bytes, decoder: codecs.IncrementalDecoder, is_complete: bool = True) -> list[str]

Split content into lines, handling incomplete lines at boundaries.

Uses a UTF-8 incremental decoder (reused across calls for streaming) for proper multi-byte character handling.

Parameters

NameTypeDescription
contentbytesRaw bytes read from a log file.
decodercodecs.IncrementalDecoderIncremental decoder instance (should be reused for streaming).
is_completeboolWhether this is the final chunk (flush the decoder).

Returns: Complete lines (without a trailing incomplete line).

tail_logs ​

python
async def tail_logs(instance: B2CInstance, options: TailLogsOptions | None = None, *, sleep: Callable[[float], Awaitable[None]] = asyncio.sleep) -> TailLogsResult

Tail log files on a B2C Commerce instance.

Continuously polls for new log content using HTTP Range requests for efficiency. Calls the on_entry callback for each new log line.

Parameters

NameTypeDescription
instanceB2CInstanceB2C instance to tail logs from.
optionsTailLogsOptions | NoneTailing options (filters, callbacks, polling interval).
sleepCallable[[float], Awaitable[None]]Injectable sleep function (Python-only, see module docstring).

Returns: Tail result with stop() control and a done awaitable.