Salesforce MCP Server
# Salesforce MCP Server
A custom-built, self-hosted MCP server that lets AI agents (Claude Desktop,
Claude Code, the MCP Inspector, or any other MCP client) connect to
Salesforce — to query, search, and modify data in an org.
> **Not a Salesforce product.** This is an independent, personal learning
> project — not affiliated with, endorsed by, or supported by Salesforce,
> Inc. Full explanation: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#not-a-salesforce-product).
> **New to MCP?** If "server," "client," and "tool call" aren't already
> familiar terms, read [docs/MCP_PRIMER.md](docs/MCP_PRIMER.md) first — five
> minutes, and everything else here will make more sense.
## Quickstart
**You'll need:** a Salesforce org with an External Client App set up — a
free [Developer Edition org](https://developer.salesforce.com/signup) works
fine — and its Consumer Key in hand. [docs/SETUP.md](docs/SETUP.md) walks
through creating that (10–15 min); do it first, then come back here.
Three ways to get a running server — pick whichever fits:
**Option A — `uvx`** (fastest; no clone, no venv; requires [uv](https://docs.astral.sh/uv/getting-started/installation/)):
```bash
export SF_LOGIN_URL=https://your-domain.my.salesforce.com
export SF_CLIENT_ID=your-client-id
uvx --from sf-mcp-server sf-mcp-login # one-time interactive login — opens your browser
uvx sf-mcp-server
```
(`sf-mcp-login` needs the explicit `--from sf-mcp-server` — `uv` only infers
the package name from a bare command when they match, and this package
provides two commands. `sf-mcp-server` matches its own package name, so it
doesn't need `--from`.)
Or drop straight into an MCP client's config (Claude Desktop's
`claude_desktop_config.json`, Claude Code's `.mcp.json`) with `"command":
"uvx", "args": ["sf-mcp-server"]` and the same env vars, **plus
`SF_PKCE_TOKEN_CACHE` set to an absolute path** (the client launches the
server from its own working directory, not wherever you ran
`sf-mcp-login`, so the default relative cache path won't be found
otherwise) — see [docs/USAGE.md](docs/USAGE.md) for the full config
example. That login step is only needed once — see
[docs/AUTHENTICATION.md](docs/AUTHENTICATION.md) for what it does and why,
and for the alternative Client Credentials Flow (`SF_CLIENT_SECRET`, no
login step) if you'd rather use a fixed service identity instead.
**Option B — Python from source** (for contributing, or if you'd rather not
use `uv`):
```bash
git clone <this-repo-url> && cd salesforce-mcp-server
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env # fill in SF_LOGIN_URL / SF_CLIENT_ID
python -m salesforce_mcp.login # one-time interactive login — opens your browser
python -m salesforce_mcp.server
```
**Option C — Docker** (no Python setup needed; requires Docker installed
*and running* — check with `docker info`):
```bash
git clone <this-repo-url> && cd salesforce-mcp-server
docker build -t salesforce-mcp-server .
docker run --rm -i \
-e SF_LOGIN_URL=https://your-domain.my.salesforce.com \
-e SF_CLIENT_ID=your-client-id \
-e SF_CLIENT_SECRET=your-client-secret \
-e SF_AUTH_FLOW=client_credentials \
-e MCP_TRANSPORT=stdio \
salesforce-mcp-server
```
Docker explicitly pins `SF_AUTH_FLOW=client_credentials` here rather than
using the default interactive login — there's no browser or display inside
a container for that flow to use. See
[docs/AUTHENTICATION.md](docs/AUTHENTICATION.md) if you actually want PKCE
in a container anyway (mount a pre-existing `.salesforce_pkce_token.json`
from the host).
Whichever you pick, that's it running. **Next:** point the
[MCP Inspector](https://github.com/modelcontextprotocol/inspector) or Claude
Desktop at it and actually try a tool — see [docs/USAGE.md](docs/USAGE.md).
Quick note on that `-e MCP_TRANSPORT=stdio` flag in Option C: **Python vs.
Docker and stdio vs. HTTP are two separate choices, not tied together** —
Python defaults to stdio and Docker's image defaults to HTTP purely for
convenience, but all four combinations actually work. See
[docs/MCP_PRIMER.md#the-two-transports-stdio-and-streamable-http](docs/MCP_PRIMER.md#the-two-transports-stdio-and-streamable-http)
for what each transport actually is and why. For hosting this on a network
instead of running it locally, see [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md).
## What it can do
- **Query & search** — `sf_query` (SOQL, auto-paginated), `sf_search` (SOSL)
- **Record CRUD** — `sf_get_record`, `sf_create_record`, `sf_update_record`,
`sf_upsert_record` (by external ID), `sf_delete_record`
- **Bulk API 2.0** — `sf_bulk_query`, `sf_bulk_load`, for record volumes too
large for the one-record-per-call REST tools above
- **Composite** — `sf_composite`, to bundle several sub-requests into one
atomic call
- **Describe/discovery** — `sf_describe_object`, `sf_list_objects` (trimmed
fields + optional `name_contains`/`custom_only` filters, so it doesn't
dump 800+ objects' full raw metadata), also available as MCP **Resources**
(`salesforce://objects`, `salesforce://schema/{sobject}`)
- **Ops** — `sf_api_usage` (quick API-limit check), `sf_org_health` (fuller
report: org info, all limits, and license seat usage)
- **Custom APIs** — `sf_call_apex_rest` calls any custom Apex REST endpoint
(`@RestResource`) your org exposes, no code changes needed — see
[docs/USAGE.md](docs/USAGE.md#calling-a-custom-apex-rest-api)
- **Prompts** — ready-made task templates for common requests:
`summarize_account`, `draft_followup_email`, `data_hygiene_check` — see
[docs/USAGE.md](docs/USAGE.md#prompts)
- **Platform events / Change Data Capture** — `sf_subscribe_platform_event`
replays a bounded batch of events from a platform event or CDC channel,
honoring Salesforce's 72-hour Pub/Sub API retention window — see
[docs/USAGE.md](docs/USAGE.md#subscribing-to-platform-events)
- **Elicitation** — confirms before an unscoped `sf_query`/`sf_search` or any
delete (`sf_delete_record`, `sf_bulk_load(operation="delete")`); disable
with `SF_ELICITATION_ENABLED=false` — see
[docs/USAGE.md](docs/USAGE.md#elicitation)
- **Two auth options** — the default interactive "Login with Salesforce"
(OAuth Authorization Code + PKCE, per-user), via `python -m
salesforce_mcp.login` or the in-session `sf_login` tool, or the OAuth
Client Credentials Flow (one fixed service identity) for headless/shared
use, switched with `SF_AUTH_FLOW=client_credentials` — see
[docs/AUTHENTICATION.md](docs/AUTHENTICATION.md)
- **Resilient by default** — retries transient (5xx / `REQUEST_LIMIT_EXCEEDED`)
Salesforce errors automatically; every other error comes back as a clean,
readable message instead of a stack trace
**Every tool above talks to a standard Salesforce API out of the box** — none
of them are specific to any one org. Two ways to add your own: call
`sf_call_apex_rest` (works today, zero code) or add a first-class tool of
your own — [docs/EXTENDING.md](docs/EXTENDING.md) is a step-by-step guide.
## Running it remotely (cloud)
The same server also runs as a container behind a network-reachable
Streamable HTTP endpoint, for when you want an agent that isn't on the same
machine to reach it. **This has been built and run locally with Docker and
confirmed working — it has not yet been deployed to a real cloud account.**
[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) has the full picture, including that
caveat up front, plus two ready-to-try recipes (GCP Cloud Run, AWS App
Runner).
## Tests
```bash
pytest tests/ -v # all Salesforce calls are mocked with respx — no live org needed
ruff check src tests
```
This is the automated suite — fast, no Salesforce org or Docker required.
There are two other, manual checks, each testing something different:
Inspector-against-a-real-org (functional — see [docs/USAGE.md](docs/USAGE.md))
and Docker-build-and-curl (plumbing only — see [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md#build-and-run-locally-first)).
## Documentation
Read in this order if you're getting started:
| # | Doc | For |
|---|---|---|
| 1 | [docs/MCP_PRIMER.md](docs/MCP_PRIMER.md) | New to MCP — what a server/client/tool call actually is |
| 2 | [docs/SETUP.md](docs/SETUP.md) | Creating the Salesforce org + integration, `.env` config |
| 3 | [docs/AUTHENTICATION.md](docs/AUTHENTICATION.md) | Both auth flows side by side — Client Credentials vs. "Login with Salesforce" (PKCE) |
| 4 | [docs/USAGE.md](docs/USAGE.md) | Running it — Claude Desktop, Claude Code, MCP Inspector, example prompts |
| 5 | [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) | Hosting it in the cloud instead of locally |
| 6 | [docs/EXTENDING.md](docs/EXTENDING.md) | Adding your own tool for a custom API |
| 7 | [docs/RELEASING.md](docs/RELEASING.md) | Maintainers only — how a PyPI release actually gets published |
| 8 | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Optional — how and why it was built this way |
## License
[MIT](LICENSE)
TDQS
Scored across 15 tools
Each tool targets a clearly distinct capability: CRUD, SOQL query, SOSL search, bulk operations, composite calls, metadata lookup, API usage, and org health. Near-overlapping tools like sf_query vs sf_bulk_query and sf_api_usage vs sf_org_health are explicitly differentiated in their descriptions.
The sf_ prefix and snake_case style are consistent, and core record operations follow a clear verb_noun pattern. Minor deviations exist with noun-style names like sf_composite, sf_api_usage, and sf_org_health, but the overall pattern remains predictable.
At 15 tools, the server sits at the upper end of the ideal range but every tool earns its place given Salesforce's broad API surface. There is no obvious redundancy or bloat; the count feels well-scoped for the domain.
The tool set covers the full standard record lifecycle (create, read, update, upsert, delete), plus query/search, metadata discovery, bulk processing, composite transactions, custom Apex REST calls, and API/org health checks. No critical dead ends exist for typical Salesforce integration workflows.