sf-mcp-server 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sf_mcp_server-0.1.0/.dockerignore +10 -0
- sf_mcp_server-0.1.0/.env.example +22 -0
- sf_mcp_server-0.1.0/.github/workflows/ci.yml +34 -0
- sf_mcp_server-0.1.0/.gitignore +11 -0
- sf_mcp_server-0.1.0/Dockerfile +21 -0
- sf_mcp_server-0.1.0/LICENSE +21 -0
- sf_mcp_server-0.1.0/PKG-INFO +207 -0
- sf_mcp_server-0.1.0/README.md +170 -0
- sf_mcp_server-0.1.0/docs/ARCHITECTURE.md +492 -0
- sf_mcp_server-0.1.0/docs/AUTHENTICATION.md +213 -0
- sf_mcp_server-0.1.0/docs/DEPLOYMENT.md +151 -0
- sf_mcp_server-0.1.0/docs/EXTENDING.md +207 -0
- sf_mcp_server-0.1.0/docs/MCP_PRIMER.md +149 -0
- sf_mcp_server-0.1.0/docs/SETUP.md +156 -0
- sf_mcp_server-0.1.0/docs/USAGE.md +457 -0
- sf_mcp_server-0.1.0/pyproject.toml +72 -0
- sf_mcp_server-0.1.0/scripts/diagnose_pubsub.py +122 -0
- sf_mcp_server-0.1.0/scripts/generate_pubsub_stubs.sh +24 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/__init__.py +1 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/auth/__init__.py +19 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/auth/client_credentials.py +33 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/auth/errors.py +7 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/auth/pkce.py +61 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/config.py +82 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/elicitation.py +47 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/errors.py +132 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/http_auth.py +26 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/login.py +209 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/prompts.py +114 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/pubsub/__init__.py +0 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/pubsub/pubsub_api.proto +413 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/pubsub/pubsub_api_pb2.py +75 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/pubsub/pubsub_api_pb2.pyi +206 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/pubsub/pubsub_api_pb2_grpc.py +422 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/pubsub_client.py +172 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/resources.py +39 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/salesforce_client.py +123 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/server.py +104 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/tools/__init__.py +0 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/tools/bulk.py +160 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/tools/composite.py +35 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/tools/custom_api.py +60 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/tools/describe.py +72 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/tools/ops.py +51 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/tools/org_health.py +60 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/tools/query.py +82 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/tools/records.py +94 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/tools/sf_login.py +49 -0
- sf_mcp_server-0.1.0/src/salesforce_mcp/tools/subscribe.py +133 -0
- sf_mcp_server-0.1.0/tests/__init__.py +0 -0
- sf_mcp_server-0.1.0/tests/conftest.py +62 -0
- sf_mcp_server-0.1.0/tests/test_auth_client_credentials.py +35 -0
- sf_mcp_server-0.1.0/tests/test_auth_factory.py +19 -0
- sf_mcp_server-0.1.0/tests/test_auth_pkce.py +106 -0
- sf_mcp_server-0.1.0/tests/test_elicitation.py +59 -0
- sf_mcp_server-0.1.0/tests/test_errors.py +62 -0
- sf_mcp_server-0.1.0/tests/test_login.py +141 -0
- sf_mcp_server-0.1.0/tests/test_prompts.py +81 -0
- sf_mcp_server-0.1.0/tests/test_pubsub_client.py +152 -0
- sf_mcp_server-0.1.0/tests/test_resources.py +53 -0
- sf_mcp_server-0.1.0/tests/test_salesforce_client.py +132 -0
- sf_mcp_server-0.1.0/tests/tools/__init__.py +0 -0
- sf_mcp_server-0.1.0/tests/tools/test_bulk.py +180 -0
- sf_mcp_server-0.1.0/tests/tools/test_composite.py +45 -0
- sf_mcp_server-0.1.0/tests/tools/test_custom_api.py +69 -0
- sf_mcp_server-0.1.0/tests/tools/test_describe.py +84 -0
- sf_mcp_server-0.1.0/tests/tools/test_ops.py +33 -0
- sf_mcp_server-0.1.0/tests/tools/test_org_health.py +65 -0
- sf_mcp_server-0.1.0/tests/tools/test_query.py +107 -0
- sf_mcp_server-0.1.0/tests/tools/test_records.py +116 -0
- sf_mcp_server-0.1.0/tests/tools/test_sf_login.py +124 -0
- sf_mcp_server-0.1.0/tests/tools/test_subscribe.py +153 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copy this file to .env and fill in real values. Never commit .env.
|
|
2
|
+
|
|
3
|
+
# --- Salesforce auth (External Client App, Client Credentials Flow) ---
|
|
4
|
+
# Your org's My Domain login URL, no trailing slash.
|
|
5
|
+
SF_LOGIN_URL=https://your-domain.my.salesforce.com
|
|
6
|
+
SF_CLIENT_ID=your-external-client-app-client-id
|
|
7
|
+
SF_CLIENT_SECRET=your-external-client-app-client-secret
|
|
8
|
+
# Optional, defaults to v61.0.
|
|
9
|
+
SF_API_VERSION=v61.0
|
|
10
|
+
|
|
11
|
+
# --- Transport ---
|
|
12
|
+
# "stdio" (default, for Claude Desktop / local MCP clients) or "http" (hosted).
|
|
13
|
+
MCP_TRANSPORT=stdio
|
|
14
|
+
|
|
15
|
+
# --- Required only when MCP_TRANSPORT=http ---
|
|
16
|
+
# Shared secret clients must send as `Authorization: Bearer <this value>`.
|
|
17
|
+
# Generate one with: python -c "import secrets; print(secrets.token_urlsafe(32))"
|
|
18
|
+
MCP_SERVER_TOKEN=
|
|
19
|
+
# Host/port to bind. PORT is honored first (the convention most container
|
|
20
|
+
# platforms inject automatically); MCP_PORT is a fallback for local use.
|
|
21
|
+
MCP_HOST=0.0.0.0
|
|
22
|
+
PORT=8080
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint-and-test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.12"
|
|
17
|
+
|
|
18
|
+
- name: Install dependencies
|
|
19
|
+
run: pip install -e ".[dev]"
|
|
20
|
+
|
|
21
|
+
- name: Lint (ruff)
|
|
22
|
+
run: ruff check src tests
|
|
23
|
+
|
|
24
|
+
- name: Test (pytest)
|
|
25
|
+
run: pytest tests/ -v
|
|
26
|
+
|
|
27
|
+
docker-build:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
needs: lint-and-test
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
|
|
33
|
+
- name: Build image
|
|
34
|
+
run: docker build -t salesforce-mcp-server:ci .
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Platform-agnostic image: no cloud-vendor SDKs baked in, just Python + a plain
|
|
2
|
+
# HTTP listener on $PORT. Runs on Cloud Run, AWS App Runner/Fargate, Fly.io,
|
|
3
|
+
# Render, a bare VM, or a k8s cluster unchanged — see docs/DEPLOYMENT.md.
|
|
4
|
+
|
|
5
|
+
FROM python:3.12-slim AS builder
|
|
6
|
+
WORKDIR /build
|
|
7
|
+
COPY pyproject.toml README.md ./
|
|
8
|
+
COPY src ./src
|
|
9
|
+
RUN pip install --no-cache-dir --prefix=/install .
|
|
10
|
+
|
|
11
|
+
FROM python:3.12-slim
|
|
12
|
+
WORKDIR /app
|
|
13
|
+
COPY --from=builder /install /usr/local
|
|
14
|
+
|
|
15
|
+
ENV MCP_TRANSPORT=http \
|
|
16
|
+
MCP_HOST=0.0.0.0 \
|
|
17
|
+
PYTHONUNBUFFERED=1
|
|
18
|
+
|
|
19
|
+
EXPOSE 8080
|
|
20
|
+
USER nobody
|
|
21
|
+
CMD ["python", "-m", "salesforce_mcp.server"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SudhakarDH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: sf-mcp-server
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An independent, open-source MCP server for Salesforce (REST, Bulk API 2.0, Composite) — not a Salesforce product.
|
|
5
|
+
Project-URL: Homepage, https://github.com/sudhakar6/salesforce-mcp-server
|
|
6
|
+
Project-URL: Repository, https://github.com/sudhakar6/salesforce-mcp-server
|
|
7
|
+
Project-URL: Documentation, https://github.com/sudhakar6/salesforce-mcp-server/tree/main/docs
|
|
8
|
+
Project-URL: Issues, https://github.com/sudhakar6/salesforce-mcp-server/issues
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai-agents,mcp,model-context-protocol,salesforce,soql
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: fastavro>=1.9
|
|
22
|
+
Requires-Dist: grpcio>=1.66
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
24
|
+
Requires-Dist: mcp[cli]>=2.1.0
|
|
25
|
+
Requires-Dist: protobuf>=5
|
|
26
|
+
Requires-Dist: python-dotenv>=1.0
|
|
27
|
+
Requires-Dist: starlette>=0.37
|
|
28
|
+
Requires-Dist: uvicorn>=0.30
|
|
29
|
+
Provides-Extra: codegen
|
|
30
|
+
Requires-Dist: grpcio-tools>=1.66; extra == 'codegen'
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# Salesforce MCP Server
|
|
39
|
+
|
|
40
|
+
A custom-built, self-hosted MCP server that lets AI agents (Claude Desktop,
|
|
41
|
+
Claude Code, the MCP Inspector, or any other MCP client) connect to
|
|
42
|
+
Salesforce — to query, search, and modify data in an org.
|
|
43
|
+
|
|
44
|
+
> **Not a Salesforce product.** This is an independent, personal learning
|
|
45
|
+
> project — not affiliated with, endorsed by, or supported by Salesforce,
|
|
46
|
+
> Inc. Full explanation: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#not-a-salesforce-product).
|
|
47
|
+
|
|
48
|
+
> **New to MCP?** If "server," "client," and "tool call" aren't already
|
|
49
|
+
> familiar terms, read [docs/MCP_PRIMER.md](docs/MCP_PRIMER.md) first — five
|
|
50
|
+
> minutes, and everything else here will make more sense.
|
|
51
|
+
|
|
52
|
+
## Quickstart
|
|
53
|
+
|
|
54
|
+
**You'll need:** a Salesforce org with an External Client App set up — a
|
|
55
|
+
free [Developer Edition org](https://developer.salesforce.com/signup) works
|
|
56
|
+
fine — and its Consumer Key in hand. [docs/SETUP.md](docs/SETUP.md) walks
|
|
57
|
+
through creating that (10–15 min); do it first, then come back here.
|
|
58
|
+
|
|
59
|
+
Three ways to get a running server — pick whichever fits:
|
|
60
|
+
|
|
61
|
+
**Option A — `uvx`** (fastest; no clone, no venv; requires [uv](https://docs.astral.sh/uv/getting-started/installation/)):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
export SF_LOGIN_URL=https://your-domain.my.salesforce.com
|
|
65
|
+
export SF_CLIENT_ID=your-client-id
|
|
66
|
+
uvx sf-mcp-login # one-time interactive login — opens your browser
|
|
67
|
+
uvx sf-mcp-server
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or drop straight into an MCP client's config (Claude Desktop's
|
|
71
|
+
`claude_desktop_config.json`, Claude Code's `.mcp.json`) with `"command":
|
|
72
|
+
"uvx", "args": ["sf-mcp-server"]` and the same env vars — see
|
|
73
|
+
[docs/USAGE.md](docs/USAGE.md) for full client config examples. That login
|
|
74
|
+
step is only needed once — see [docs/AUTHENTICATION.md](docs/AUTHENTICATION.md)
|
|
75
|
+
for what it does and why, and for the alternative Client Credentials Flow
|
|
76
|
+
(`SF_CLIENT_SECRET`, no login step) if you'd rather use a fixed service
|
|
77
|
+
identity instead.
|
|
78
|
+
|
|
79
|
+
**Option B — Python from source** (for contributing, or if you'd rather not
|
|
80
|
+
use `uv`):
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git clone <this-repo-url> && cd salesforce-mcp-server
|
|
84
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
85
|
+
pip install -e ".[dev]"
|
|
86
|
+
cp .env.example .env # fill in SF_LOGIN_URL / SF_CLIENT_ID
|
|
87
|
+
python -m salesforce_mcp.login # one-time interactive login — opens your browser
|
|
88
|
+
python -m salesforce_mcp.server
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Option C — Docker** (no Python setup needed; requires Docker installed
|
|
92
|
+
*and running* — check with `docker info`):
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
git clone <this-repo-url> && cd salesforce-mcp-server
|
|
96
|
+
docker build -t salesforce-mcp-server .
|
|
97
|
+
docker run --rm -i \
|
|
98
|
+
-e SF_LOGIN_URL=https://your-domain.my.salesforce.com \
|
|
99
|
+
-e SF_CLIENT_ID=your-client-id \
|
|
100
|
+
-e SF_CLIENT_SECRET=your-client-secret \
|
|
101
|
+
-e SF_AUTH_FLOW=client_credentials \
|
|
102
|
+
-e MCP_TRANSPORT=stdio \
|
|
103
|
+
salesforce-mcp-server
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Docker explicitly pins `SF_AUTH_FLOW=client_credentials` here rather than
|
|
107
|
+
using the default interactive login — there's no browser or display inside
|
|
108
|
+
a container for that flow to use. See
|
|
109
|
+
[docs/AUTHENTICATION.md](docs/AUTHENTICATION.md) if you actually want PKCE
|
|
110
|
+
in a container anyway (mount a pre-existing `.salesforce_pkce_token.json`
|
|
111
|
+
from the host).
|
|
112
|
+
|
|
113
|
+
Whichever you pick, that's it running. **Next:** point the
|
|
114
|
+
[MCP Inspector](https://github.com/modelcontextprotocol/inspector) or Claude
|
|
115
|
+
Desktop at it and actually try a tool — see [docs/USAGE.md](docs/USAGE.md).
|
|
116
|
+
|
|
117
|
+
Quick note on that `-e MCP_TRANSPORT=stdio` flag in Option C: **Python vs.
|
|
118
|
+
Docker and stdio vs. HTTP are two separate choices, not tied together** —
|
|
119
|
+
Python defaults to stdio and Docker's image defaults to HTTP purely for
|
|
120
|
+
convenience, but all four combinations actually work. See
|
|
121
|
+
[docs/MCP_PRIMER.md#the-two-transports-stdio-and-streamable-http](docs/MCP_PRIMER.md#the-two-transports-stdio-and-streamable-http)
|
|
122
|
+
for what each transport actually is and why. For hosting this on a network
|
|
123
|
+
instead of running it locally, see [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md).
|
|
124
|
+
|
|
125
|
+
## What it can do
|
|
126
|
+
|
|
127
|
+
- **Query & search** — `sf_query` (SOQL, auto-paginated), `sf_search` (SOSL)
|
|
128
|
+
- **Record CRUD** — `sf_get_record`, `sf_create_record`, `sf_update_record`,
|
|
129
|
+
`sf_upsert_record` (by external ID), `sf_delete_record`
|
|
130
|
+
- **Bulk API 2.0** — `sf_bulk_query`, `sf_bulk_load`, for record volumes too
|
|
131
|
+
large for the one-record-per-call REST tools above
|
|
132
|
+
- **Composite** — `sf_composite`, to bundle several sub-requests into one
|
|
133
|
+
atomic call
|
|
134
|
+
- **Describe/discovery** — `sf_describe_object`, `sf_list_objects` (trimmed
|
|
135
|
+
fields + optional `name_contains`/`custom_only` filters, so it doesn't
|
|
136
|
+
dump 800+ objects' full raw metadata), also available as MCP **Resources**
|
|
137
|
+
(`salesforce://objects`, `salesforce://schema/{sobject}`)
|
|
138
|
+
- **Ops** — `sf_api_usage` (quick API-limit check), `sf_org_health` (fuller
|
|
139
|
+
report: org info, all limits, and license seat usage)
|
|
140
|
+
- **Custom APIs** — `sf_call_apex_rest` calls any custom Apex REST endpoint
|
|
141
|
+
(`@RestResource`) your org exposes, no code changes needed — see
|
|
142
|
+
[docs/USAGE.md](docs/USAGE.md#calling-a-custom-apex-rest-api)
|
|
143
|
+
- **Prompts** — ready-made task templates for common requests:
|
|
144
|
+
`summarize_account`, `draft_followup_email`, `data_hygiene_check` — see
|
|
145
|
+
[docs/USAGE.md](docs/USAGE.md#prompts)
|
|
146
|
+
- **Platform events / Change Data Capture** — `sf_subscribe_platform_event`
|
|
147
|
+
replays a bounded batch of events from a platform event or CDC channel,
|
|
148
|
+
honoring Salesforce's 72-hour Pub/Sub API retention window — see
|
|
149
|
+
[docs/USAGE.md](docs/USAGE.md#subscribing-to-platform-events)
|
|
150
|
+
- **Elicitation** — confirms before an unscoped `sf_query`/`sf_search` or any
|
|
151
|
+
delete (`sf_delete_record`, `sf_bulk_load(operation="delete")`); disable
|
|
152
|
+
with `SF_ELICITATION_ENABLED=false` — see
|
|
153
|
+
[docs/USAGE.md](docs/USAGE.md#elicitation)
|
|
154
|
+
- **Two auth options** — the default interactive "Login with Salesforce"
|
|
155
|
+
(OAuth Authorization Code + PKCE, per-user), via `python -m
|
|
156
|
+
salesforce_mcp.login` or the in-session `sf_login` tool, or the OAuth
|
|
157
|
+
Client Credentials Flow (one fixed service identity) for headless/shared
|
|
158
|
+
use, switched with `SF_AUTH_FLOW=client_credentials` — see
|
|
159
|
+
[docs/AUTHENTICATION.md](docs/AUTHENTICATION.md)
|
|
160
|
+
- **Resilient by default** — retries transient (5xx / `REQUEST_LIMIT_EXCEEDED`)
|
|
161
|
+
Salesforce errors automatically; every other error comes back as a clean,
|
|
162
|
+
readable message instead of a stack trace
|
|
163
|
+
|
|
164
|
+
**Every tool above talks to a standard Salesforce API out of the box** — none
|
|
165
|
+
of them are specific to any one org. Two ways to add your own: call
|
|
166
|
+
`sf_call_apex_rest` (works today, zero code) or add a first-class tool of
|
|
167
|
+
your own — [docs/EXTENDING.md](docs/EXTENDING.md) is a step-by-step guide.
|
|
168
|
+
|
|
169
|
+
## Running it remotely (cloud)
|
|
170
|
+
|
|
171
|
+
The same server also runs as a container behind a network-reachable
|
|
172
|
+
Streamable HTTP endpoint, for when you want an agent that isn't on the same
|
|
173
|
+
machine to reach it. **This has been built and run locally with Docker and
|
|
174
|
+
confirmed working — it has not yet been deployed to a real cloud account.**
|
|
175
|
+
[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) has the full picture, including that
|
|
176
|
+
caveat up front, plus two ready-to-try recipes (GCP Cloud Run, AWS App
|
|
177
|
+
Runner).
|
|
178
|
+
|
|
179
|
+
## Tests
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
pytest tests/ -v # all Salesforce calls are mocked with respx — no live org needed
|
|
183
|
+
ruff check src tests
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
This is the automated suite — fast, no Salesforce org or Docker required.
|
|
187
|
+
There are two other, manual checks, each testing something different:
|
|
188
|
+
Inspector-against-a-real-org (functional — see [docs/USAGE.md](docs/USAGE.md))
|
|
189
|
+
and Docker-build-and-curl (plumbing only — see [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md#build-and-run-locally-first)).
|
|
190
|
+
|
|
191
|
+
## Documentation
|
|
192
|
+
|
|
193
|
+
Read in this order if you're getting started:
|
|
194
|
+
|
|
195
|
+
| # | Doc | For |
|
|
196
|
+
|---|---|---|
|
|
197
|
+
| 1 | [docs/MCP_PRIMER.md](docs/MCP_PRIMER.md) | New to MCP — what a server/client/tool call actually is |
|
|
198
|
+
| 2 | [docs/SETUP.md](docs/SETUP.md) | Creating the Salesforce org + integration, `.env` config |
|
|
199
|
+
| 3 | [docs/AUTHENTICATION.md](docs/AUTHENTICATION.md) | Both auth flows side by side — Client Credentials vs. "Login with Salesforce" (PKCE) |
|
|
200
|
+
| 4 | [docs/USAGE.md](docs/USAGE.md) | Running it — Claude Desktop, Claude Code, MCP Inspector, example prompts |
|
|
201
|
+
| 5 | [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) | Hosting it in the cloud instead of locally |
|
|
202
|
+
| 6 | [docs/EXTENDING.md](docs/EXTENDING.md) | Adding your own tool for a custom API |
|
|
203
|
+
| 7 | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Optional — how and why it was built this way |
|
|
204
|
+
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Salesforce MCP Server
|
|
2
|
+
|
|
3
|
+
A custom-built, self-hosted MCP server that lets AI agents (Claude Desktop,
|
|
4
|
+
Claude Code, the MCP Inspector, or any other MCP client) connect to
|
|
5
|
+
Salesforce — to query, search, and modify data in an org.
|
|
6
|
+
|
|
7
|
+
> **Not a Salesforce product.** This is an independent, personal learning
|
|
8
|
+
> project — not affiliated with, endorsed by, or supported by Salesforce,
|
|
9
|
+
> Inc. Full explanation: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#not-a-salesforce-product).
|
|
10
|
+
|
|
11
|
+
> **New to MCP?** If "server," "client," and "tool call" aren't already
|
|
12
|
+
> familiar terms, read [docs/MCP_PRIMER.md](docs/MCP_PRIMER.md) first — five
|
|
13
|
+
> minutes, and everything else here will make more sense.
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
16
|
+
|
|
17
|
+
**You'll need:** a Salesforce org with an External Client App set up — a
|
|
18
|
+
free [Developer Edition org](https://developer.salesforce.com/signup) works
|
|
19
|
+
fine — and its Consumer Key in hand. [docs/SETUP.md](docs/SETUP.md) walks
|
|
20
|
+
through creating that (10–15 min); do it first, then come back here.
|
|
21
|
+
|
|
22
|
+
Three ways to get a running server — pick whichever fits:
|
|
23
|
+
|
|
24
|
+
**Option A — `uvx`** (fastest; no clone, no venv; requires [uv](https://docs.astral.sh/uv/getting-started/installation/)):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
export SF_LOGIN_URL=https://your-domain.my.salesforce.com
|
|
28
|
+
export SF_CLIENT_ID=your-client-id
|
|
29
|
+
uvx sf-mcp-login # one-time interactive login — opens your browser
|
|
30
|
+
uvx sf-mcp-server
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or drop straight into an MCP client's config (Claude Desktop's
|
|
34
|
+
`claude_desktop_config.json`, Claude Code's `.mcp.json`) with `"command":
|
|
35
|
+
"uvx", "args": ["sf-mcp-server"]` and the same env vars — see
|
|
36
|
+
[docs/USAGE.md](docs/USAGE.md) for full client config examples. That login
|
|
37
|
+
step is only needed once — see [docs/AUTHENTICATION.md](docs/AUTHENTICATION.md)
|
|
38
|
+
for what it does and why, and for the alternative Client Credentials Flow
|
|
39
|
+
(`SF_CLIENT_SECRET`, no login step) if you'd rather use a fixed service
|
|
40
|
+
identity instead.
|
|
41
|
+
|
|
42
|
+
**Option B — Python from source** (for contributing, or if you'd rather not
|
|
43
|
+
use `uv`):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
git clone <this-repo-url> && cd salesforce-mcp-server
|
|
47
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
48
|
+
pip install -e ".[dev]"
|
|
49
|
+
cp .env.example .env # fill in SF_LOGIN_URL / SF_CLIENT_ID
|
|
50
|
+
python -m salesforce_mcp.login # one-time interactive login — opens your browser
|
|
51
|
+
python -m salesforce_mcp.server
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Option C — Docker** (no Python setup needed; requires Docker installed
|
|
55
|
+
*and running* — check with `docker info`):
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone <this-repo-url> && cd salesforce-mcp-server
|
|
59
|
+
docker build -t salesforce-mcp-server .
|
|
60
|
+
docker run --rm -i \
|
|
61
|
+
-e SF_LOGIN_URL=https://your-domain.my.salesforce.com \
|
|
62
|
+
-e SF_CLIENT_ID=your-client-id \
|
|
63
|
+
-e SF_CLIENT_SECRET=your-client-secret \
|
|
64
|
+
-e SF_AUTH_FLOW=client_credentials \
|
|
65
|
+
-e MCP_TRANSPORT=stdio \
|
|
66
|
+
salesforce-mcp-server
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Docker explicitly pins `SF_AUTH_FLOW=client_credentials` here rather than
|
|
70
|
+
using the default interactive login — there's no browser or display inside
|
|
71
|
+
a container for that flow to use. See
|
|
72
|
+
[docs/AUTHENTICATION.md](docs/AUTHENTICATION.md) if you actually want PKCE
|
|
73
|
+
in a container anyway (mount a pre-existing `.salesforce_pkce_token.json`
|
|
74
|
+
from the host).
|
|
75
|
+
|
|
76
|
+
Whichever you pick, that's it running. **Next:** point the
|
|
77
|
+
[MCP Inspector](https://github.com/modelcontextprotocol/inspector) or Claude
|
|
78
|
+
Desktop at it and actually try a tool — see [docs/USAGE.md](docs/USAGE.md).
|
|
79
|
+
|
|
80
|
+
Quick note on that `-e MCP_TRANSPORT=stdio` flag in Option C: **Python vs.
|
|
81
|
+
Docker and stdio vs. HTTP are two separate choices, not tied together** —
|
|
82
|
+
Python defaults to stdio and Docker's image defaults to HTTP purely for
|
|
83
|
+
convenience, but all four combinations actually work. See
|
|
84
|
+
[docs/MCP_PRIMER.md#the-two-transports-stdio-and-streamable-http](docs/MCP_PRIMER.md#the-two-transports-stdio-and-streamable-http)
|
|
85
|
+
for what each transport actually is and why. For hosting this on a network
|
|
86
|
+
instead of running it locally, see [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md).
|
|
87
|
+
|
|
88
|
+
## What it can do
|
|
89
|
+
|
|
90
|
+
- **Query & search** — `sf_query` (SOQL, auto-paginated), `sf_search` (SOSL)
|
|
91
|
+
- **Record CRUD** — `sf_get_record`, `sf_create_record`, `sf_update_record`,
|
|
92
|
+
`sf_upsert_record` (by external ID), `sf_delete_record`
|
|
93
|
+
- **Bulk API 2.0** — `sf_bulk_query`, `sf_bulk_load`, for record volumes too
|
|
94
|
+
large for the one-record-per-call REST tools above
|
|
95
|
+
- **Composite** — `sf_composite`, to bundle several sub-requests into one
|
|
96
|
+
atomic call
|
|
97
|
+
- **Describe/discovery** — `sf_describe_object`, `sf_list_objects` (trimmed
|
|
98
|
+
fields + optional `name_contains`/`custom_only` filters, so it doesn't
|
|
99
|
+
dump 800+ objects' full raw metadata), also available as MCP **Resources**
|
|
100
|
+
(`salesforce://objects`, `salesforce://schema/{sobject}`)
|
|
101
|
+
- **Ops** — `sf_api_usage` (quick API-limit check), `sf_org_health` (fuller
|
|
102
|
+
report: org info, all limits, and license seat usage)
|
|
103
|
+
- **Custom APIs** — `sf_call_apex_rest` calls any custom Apex REST endpoint
|
|
104
|
+
(`@RestResource`) your org exposes, no code changes needed — see
|
|
105
|
+
[docs/USAGE.md](docs/USAGE.md#calling-a-custom-apex-rest-api)
|
|
106
|
+
- **Prompts** — ready-made task templates for common requests:
|
|
107
|
+
`summarize_account`, `draft_followup_email`, `data_hygiene_check` — see
|
|
108
|
+
[docs/USAGE.md](docs/USAGE.md#prompts)
|
|
109
|
+
- **Platform events / Change Data Capture** — `sf_subscribe_platform_event`
|
|
110
|
+
replays a bounded batch of events from a platform event or CDC channel,
|
|
111
|
+
honoring Salesforce's 72-hour Pub/Sub API retention window — see
|
|
112
|
+
[docs/USAGE.md](docs/USAGE.md#subscribing-to-platform-events)
|
|
113
|
+
- **Elicitation** — confirms before an unscoped `sf_query`/`sf_search` or any
|
|
114
|
+
delete (`sf_delete_record`, `sf_bulk_load(operation="delete")`); disable
|
|
115
|
+
with `SF_ELICITATION_ENABLED=false` — see
|
|
116
|
+
[docs/USAGE.md](docs/USAGE.md#elicitation)
|
|
117
|
+
- **Two auth options** — the default interactive "Login with Salesforce"
|
|
118
|
+
(OAuth Authorization Code + PKCE, per-user), via `python -m
|
|
119
|
+
salesforce_mcp.login` or the in-session `sf_login` tool, or the OAuth
|
|
120
|
+
Client Credentials Flow (one fixed service identity) for headless/shared
|
|
121
|
+
use, switched with `SF_AUTH_FLOW=client_credentials` — see
|
|
122
|
+
[docs/AUTHENTICATION.md](docs/AUTHENTICATION.md)
|
|
123
|
+
- **Resilient by default** — retries transient (5xx / `REQUEST_LIMIT_EXCEEDED`)
|
|
124
|
+
Salesforce errors automatically; every other error comes back as a clean,
|
|
125
|
+
readable message instead of a stack trace
|
|
126
|
+
|
|
127
|
+
**Every tool above talks to a standard Salesforce API out of the box** — none
|
|
128
|
+
of them are specific to any one org. Two ways to add your own: call
|
|
129
|
+
`sf_call_apex_rest` (works today, zero code) or add a first-class tool of
|
|
130
|
+
your own — [docs/EXTENDING.md](docs/EXTENDING.md) is a step-by-step guide.
|
|
131
|
+
|
|
132
|
+
## Running it remotely (cloud)
|
|
133
|
+
|
|
134
|
+
The same server also runs as a container behind a network-reachable
|
|
135
|
+
Streamable HTTP endpoint, for when you want an agent that isn't on the same
|
|
136
|
+
machine to reach it. **This has been built and run locally with Docker and
|
|
137
|
+
confirmed working — it has not yet been deployed to a real cloud account.**
|
|
138
|
+
[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) has the full picture, including that
|
|
139
|
+
caveat up front, plus two ready-to-try recipes (GCP Cloud Run, AWS App
|
|
140
|
+
Runner).
|
|
141
|
+
|
|
142
|
+
## Tests
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
pytest tests/ -v # all Salesforce calls are mocked with respx — no live org needed
|
|
146
|
+
ruff check src tests
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
This is the automated suite — fast, no Salesforce org or Docker required.
|
|
150
|
+
There are two other, manual checks, each testing something different:
|
|
151
|
+
Inspector-against-a-real-org (functional — see [docs/USAGE.md](docs/USAGE.md))
|
|
152
|
+
and Docker-build-and-curl (plumbing only — see [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md#build-and-run-locally-first)).
|
|
153
|
+
|
|
154
|
+
## Documentation
|
|
155
|
+
|
|
156
|
+
Read in this order if you're getting started:
|
|
157
|
+
|
|
158
|
+
| # | Doc | For |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| 1 | [docs/MCP_PRIMER.md](docs/MCP_PRIMER.md) | New to MCP — what a server/client/tool call actually is |
|
|
161
|
+
| 2 | [docs/SETUP.md](docs/SETUP.md) | Creating the Salesforce org + integration, `.env` config |
|
|
162
|
+
| 3 | [docs/AUTHENTICATION.md](docs/AUTHENTICATION.md) | Both auth flows side by side — Client Credentials vs. "Login with Salesforce" (PKCE) |
|
|
163
|
+
| 4 | [docs/USAGE.md](docs/USAGE.md) | Running it — Claude Desktop, Claude Code, MCP Inspector, example prompts |
|
|
164
|
+
| 5 | [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) | Hosting it in the cloud instead of locally |
|
|
165
|
+
| 6 | [docs/EXTENDING.md](docs/EXTENDING.md) | Adding your own tool for a custom API |
|
|
166
|
+
| 7 | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Optional — how and why it was built this way |
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
[MIT](LICENSE)
|