Skip to Content
CLIMCP Server

MCP Server

The sfdt Model Context Protocol (MCP) server exposes Salesforce release-management, testing, and governance tools directly to agentic workflows — Claude Code, Cursor, Copilot, or any standard MCP client.

Start the server

sfdt mcp start

This starts a JSON-RPC 2.0 stdio stream on standard input/output. All operational logs are routed to stderr to keep the RPC channel clean. The server is stateless per request — every tool call shells out to the sfdt CLI.

Configuration

Configure it under the mcp key in .sfdt/config.json:

{ "mcp": { "enabled": true, "parking": { "enabled": true, "thresholdBytes": 50000, "ttlSeconds": 86400, "cacheScope": "session" } } }

Pinning which projects a server serves

Every tool accepts a projectRoot, and that value is chosen by the model — while SFDT’s AI surfaces feed that model untrusted org content (Apex compile errors, flow metadata, deploy failure text). So a call you read as “query the current project” can name a different checkout and run against that org, with the tool list still presenting it as read-only.

Since 0.26.0 the default depends on how the server was launched — which is what separates the safe case from the abusable one.

LaunchedprojectRoot behaviour
Inside a project (an sfdx-project.json exists at or above the launch directory)Serves that project only. projectRoot may restate the root, or name any path under it, but not point elsewhere.
Outside any project (neutral)Unrestricted. Routing every call is this mode’s entire purpose.

So a multi-project server is one you start outside any project — which is what it already was. To keep a project-bound server serving several checkouts, name them:

export SFDT_MCP_PROJECT_ROOTS="/work/customer-a:/work/customer-b"

Colon-separated absolute paths. The allowlist wins wherever it is set.

Two details worth knowing:

  • Being “inside a project” is decided by finding the project, not by whether its configuration loads. A project whose .sfdt/config.json or sfdx-project.json is malformed still binds the server — those files are committed and arrive with whatever repo was cloned, so the alternative would let a repository decide that the server reviewing it should be unrestricted.
  • Paths are compared physically, after resolving symlinks. A committed symlink pointing out of the project does not satisfy the check.

Upgrading from 0.25.0. If you start the server inside one project and route calls to another, that now fails with “outside this server’s project”. Either start it outside any project, or list the roots in SFDT_MCP_PROJECT_ROOTS. 0.25.0 shipped this as opt-in only, so the default was unchanged and the model still chose the root.

Added in 0.25.0. Also in 0.25.0: sfdt_retrofit requires confirmExecution on both paths (without --execute it still retrieved over your checkout and committed), and sfdt_pr_comment takes a PR number rather than a URL — a URL names a repository too — redacts its body, and requires confirmation instead of presenting as read-only.

Exposed tools

Pre-deployment validation

ToolArguments
sfdt_preflightstrict (boolean, optional)
sfdt_validatetargetOrg (required), manifest, testLevel, testClasses
sfdt_manifest_from_gitbase (default main), head (default HEAD), package, name — build package.xml + destructiveChanges.xml from a git diff. Read-only.

Deployment & rollback (safety-gated)

Modifying operations require confirmExecution: true as an explicit safety gate. Omit it (or set it false) and the tool returns a validation error and aborts.

confirmExecution is not the control for path arguments. It authorises the operation, while the model still supplies the argument — so since 0.23.1 every path-shaped argument is contained independently of it:

  • file (sfdt_apex_run) and manifest (sfdt_validate, sfdt_deploy) must be relative paths that resolve inside the project. Absolute paths and .. segments are rejected.
  • set (sfdt_data_export / _import / _load / _delete) must be a bare identifier matching ^[A-Za-z0-9][A-Za-z0-9_-]*$ — no dots, slashes, or leading -.

A rejected argument returns an error and the tool does not run. This exists because MCP arguments are chosen by a model, and the CLI’s AI surfaces feed that model untrusted org content — Apex compile errors, flow metadata, deploy failure text — so a path argument is untrusted input.

ToolArguments
sfdt_deploytargetOrg (req), manifest, testLevel, testClasses, destructiveTiming, smart, deltaBase, deltaHead, dryRun, confirmExecution
sfdt_quick_deployvalidationJobId (req), targetOrg (req), confirmExecution (req)
sfdt_rollbackconfirmExecution (req)
sfdt_retrofitsource (req), target, metadata, execute; real deploys require confirmExecution
sfdt_releaseversion, package (default all), name, confirmExecution (req) — builds the release manifest + notes and writes artifacts to the repo

For sfdt_deploy, a validate-only run (dryRun: true) no longer requires confirmExecution; a real deploy still does. See Smart Deploy.

Analysis & observe

ToolArguments
sfdt_driftorg (optional)
sfdt_comparesource (req), target (req)
sfdt_qualitygenerateStubs, fixPlan, apexGuru (org-side ApexGuru check only; license-gated, degrades to skipped), org (target for apexGuru)
sfdt_logstypepreflight | drift | deploy | rollback | quality
sfdt_coverageorg (optional) — org-wide + per-class Apex coverage. Read-only.
sfdt_scanorg (optional) — full metadata inventory of the org. Read-only.
sfdt_dependenciesname (req) — component name; org (optional). What it references and what references it. Read-only.
sfdt_flow_scanorg (optional) — Flow quality analysis via @sfdt/flow-core (fetches each active FlowDefinition version). Read-only.
sfdt_docsai (boolean) — generate MkDocs project docs (objects/fields, Apex, Flows) with an optional AI overview and a Mermaid ER diagram. Read-only.
sfdt_testclassNames (array, optional) — run Apex tests via the enhanced runner; limit to specific classes or use the configured set. Consumes org test resources; not metadata-mutating.
sfdt_historytype (optional filter — audit | monitor | quality | test-run | deploy | agent-test …), limit (default 30) — recent runs from the local run index. Read-only.
sfdt_soql_searchterm, category (all | custom | standard), limit, org — find sObjects by name substring. Read-only.
sfdt_soql_describesobject (req), filter, tooling, org — fields, picklists, references, child relationships. Read-only.
sfdt_soql_validatequery (req), org — local checks + an org LIMIT 0 round-trip; never executes rows. Read-only.
sfdt_soql_planquery (req), org — org query plans via the REST explain endpoint; never executes the query. Read-only.
sfdt_soql_queryquery (req), limit, tooling, org — bounded SOQL execution (soql.defaultLimit/soql.maxLimit, never unbounded). Read-only.
sfdt_apex_logsorg, logId (retrieve one log body), limit (default 20) — list recent Apex debug logs or fetch one by Id. Read-only.

Scratch orgs & data (safety-gated)

The mutating tools here take the same confirmExecution: true gate as deployment. sfdt_data_export and sfdt_scratch_pool with action: "status" are read-only and need no gate.

ToolArguments
sfdt_scratch_createalias, days (1–30), confirmExecution (req)
sfdt_scratch_deletetarget (req — alias or username), confirmExecution (req)
sfdt_scratch_poolaction (req — status read-only | fill mutating), size, confirmExecution (req for fill)
sfdt_data_exportset (req), org — export a configured data set to local files. Read-only against the org.
sfdt_data_importset (req), org, confirmExecution (req) — write records to the org
sfdt_data_deleteset (req), org, confirmExecution (req) — bulk-delete records in the org
sfdt_apex_traceaction (req — list read-only | start/stop mutating), org, user, duration (minutes), debugLevel, all, confirmExecution (req for start/stop) — manage USER_DEBUG trace flags
sfdt_apex_runorg, file (project-relative path) or apexCode (inline), confirmExecution (req) — execute Anonymous Apex in the org

Org health & notifications

ToolArguments
sfdt_auditcheck — any audit check id or all. Includes inactive-flows, unused-permsets, connected-apps, field-descriptions, apex-unreferenced, lint-access, inactive-validations, inactive-workflows, lint-access-fields
sfdt_monitorchecklimits, errors, health, backup, org-info, deploy-history, deprecated-api, flow-errors, or all
sfdt_notifyPush the latest audit/monitor snapshot to configured channels (type: audit | monitor)
sfdt_pr_commenttype (audit | monitor), pr (a PR number in the current repository — a URL is refused, since a URL names a repository too), confirmExecution (req) — post the latest snapshot as a PR comment. The body is redacted before it leaves the machine.

Context budget governance

ToolArguments
sfdt_get_parked_resultref (req) — e.g. parked://<uuid>

Context parking

When a tool result exceeds mcp.parking.thresholdBytes (default 50 KB), the server writes the full payload to .sfdt/cache/parked/<uuid>.json and returns a lightweight envelope instead:

{ "_parked": true, "ref": "parked://<uuid>", "byteSize": 123456, "rowCount": 42, "preview": "...", "ttlMs": 86400000, "cacheScope": "session" }

The envelope carries SEP-2549 cache metadata — ttlMs + cacheScope (replacing the older expiresAt) — aligned with the MCP 2026-07-28 release candidate. Treat ttlMs as relative to when you received the envelope. Retrieve the full payload with sfdt_get_parked_result.

Purge expired parked files:

sfdt mcp cleanup

Client setup

Claude Code

{ "mcpServers": { "sfdt": { "command": "node", "args": ["/absolute/path/to/sfdt/bin/sfdt.js", "mcp", "start"] } } }

Cursor

  1. Settings → Features → MCP → + Add New MCP Server.
  2. Name: sfdt; Type: stdio.
  3. Command: node /absolute/path/to/sfdt/bin/sfdt.js mcp start.
Last updated on