sf-mcp-proxy
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@sf-mcp-proxyQuery Salesforce for recent opportunities"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
sf-mcp-proxy (Python)
Disclaimer. Independent, unofficial project — not affiliated with, endorsed by, or sponsored by Salesforce, Inc. "Salesforce" and related marks are trademarks of Salesforce, Inc.
Salesforce's Hosted MCP servers (sobject-reads, sobject-all, flows,
invocable-actions, …) sit behind OAuth 2.0, and Salesforce does not support
Dynamic Client Registration — so every client must be pre-wired with a Consumer
Key from a Salesforce External Client App and run the full Authorization
Code + PKCE browser flow before it can call a single tool.
sf-mcp-proxy does that once and then gives you:
an importable library — an authenticated, in-process
mcp.ClientSessionthat LangChain, LangGraph, and Google ADK can build agents on with no subprocess and no second OAuth layer;a CLI to log in, inspect, and call tools;
a proxy (
sf-mcp-proxy proxy) that re-exposes one Salesforce MCP server as a plain MCP server (stdio or Streamable HTTP), optionally behind its own OAuth;a chat loop — an LLM in a tool-use loop over one server's tools.
This is a Python port of the TypeScript
sf-mcp-proxy. The token cache
directory, cache-file naming, StoredSession JSON shape, and encryption-at-rest
envelope are byte-compatible with the TS CLI, so the two share
~/.sf-mcp-proxy/.
The OAuth machinery (RFC 9728 discovery, PKCE, the resource indicator, token
exchange/refresh) is handled by the official mcp
SDK; this package supplies a Salesforce-specific OAuthClientProvider.
API scope. Salesforce owns the Hosted MCP API and can change it without notice; behaviour may drift on later releases and support is best-effort.
Install
pip install sf-mcp-proxy # library + CLI + proxy
pip install "sf-mcp-proxy[langchain]" # + LangChain / LangGraph adapters
pip install "sf-mcp-proxy[adk]" # + Google ADK toolset
pip install "sf-mcp-proxy[chat]" # + `sf-mcp-proxy chat`
pip install "sf-mcp-proxy[all]"Python 3.10–3.13.
Related MCP server: SFCC MCP Server
1. Create a Salesforce External Client App (ECA)
Do this once, in the org you want to connect to.
Setup → External Client App Manager → New External Client App.
Basic Information — name it, set a contact email, Distribution State Local.
API (Enable OAuth Settings) → Enable OAuth:
Callback URL:
http://localhost:8000/callback(must exactly matchOAUTH_REDIRECT_URI).OAuth Scopes: Access MCP servers (
mcp_api) and Perform requests at any time (refresh_token).Check Require PKCE.
Uncheck Require secret for Web Server Flow / Refresh Token Flow to run as a public client (PKCE only). If you leave them checked, set
SF_CONSUMER_SECRET.
Policies → OAuth Policies: set Permitted Users, and Issue JWT-based access tokens for named users (so
whoamican decode claims).Settings → OAuth Settings → Consumer Key — copy it.
Wait ~30 min for the ECA to propagate before first use.
2. Configure
Everything can be passed to the library explicitly, or read from the environment
/ a .env file in the working directory.
Variable | Notes |
| Required. Consumer Key from the ECA. |
| Only if the ECA still requires a secret (→ |
|
|
| Must match an ECA Callback URL. Default |
| Loopback port. Default |
| Discovery fallback. Default |
| Your My Domain host (e.g. |
| Encrypt the token cache at rest (scrypt + AES-256-GCM). |
|
|
|
|
servers.json (searched in the working directory, then
~/.sf-mcp-proxy/servers.json). The reliable way is to copy the exact URL from
Salesforce Setup → MCP servers into url:
{
"servers": [
{ "name": "sobject-reads", "url": "https://api.salesforce.com/platform/mcp/v1/sandbox/platform/sobject-reads" },
{ "name": "flows", "env": "platform" }
]
}If url is omitted the URL is built as
https://api.salesforce.com/platform/mcp/v1/{group}/{name} (production) or
…/mcp/v1/sandbox/{group}/{name} (sandbox); group defaults to platform.
When absent entirely, a built-in default list is used (sobject-reads,
sobject-all, flows, …). -s/--server also accepts a full URL directly.
3. CLI
sf-mcp-proxy login -s sobject-reads -e sandbox # browser OAuth, caches tokens
sf-mcp-proxy tools -s sobject-reads
sf-mcp-proxy call query -s sobject-reads -a '{"soql":"SELECT Id, Name FROM Account LIMIT 3"}'Command | What it does |
| list configured servers + resolved URLs |
| browser OAuth + PKCE; prints the cache path |
| delete the cached session file |
| decode & print the cached access token's JWT claims |
| list tools, or raw tool defs as JSON |
| print |
| call one tool, print the result |
| interactive: |
| LLM tool-use loop |
| re-expose one server as MCP |
| list / revoke clients registered against |
-s/--server may be omitted if exactly one server is configured. -e/--env
overrides the tier for that run.
4. Library
import asyncio
from sf_mcp_proxy import SalesforceMCP
async def main():
# One-time interactive login (opens a browser, caches tokens).
await SalesforceMCP.login("sobject-reads", env="sandbox")
# Authenticated session. Uses the cached token, auto-refreshes, and by
# default does NOT open a browser (interactive=False).
async with SalesforceMCP.connect("sobject-reads", env="sandbox") as sf:
tools = await sf.list_tools() # list[mcp.types.Tool]
result = await sf.call_tool("query", {"soql": "SELECT Id FROM Account LIMIT 1"})
session = sf.session # raw mcp.ClientSession
asyncio.run(main())connect() accepts the same server spec as the CLI (name | bare name | full URL),
plus env=, group=, and explicit consumer_key= / consumer_secret= /
settings= / passphrase= overrides so a library caller never needs a .env
file. list_tools / call_tool transparently reconnect once on an authorization
failure (a port of the TS withReauth). SalesforceMCP.connect_sync(...) is a
blocking facade for notebooks and the CLI.
5. Framework integrations
LangChain / LangGraph (sf-mcp-proxy[langchain])
from sf_mcp_proxy.integrations.langchain import SalesforceToolkit
async with SalesforceToolkit("sobject-reads", env="sandbox") as tools:
# `tools` is list[langchain_core.tools.BaseTool] — pass to any agent
...from sf_mcp_proxy.integrations.langgraph import create_salesforce_agent
agent = await create_salesforce_agent(
model="anthropic:claude-opus-5",
server="sobject-reads",
env="sandbox",
)
async for step in agent.astream({"messages": [("user", "how many open opps?")]}):
...
await agent.aclose()load_salesforce_tools(sf) wraps a live session you already own. Tier B —
salesforce_stdio_connection(...) — returns a langchain-mcp-adapters connection
pointed at a spawned sf-mcp-proxy proxy.
Google ADK (sf-mcp-proxy[adk])
from google.adk.agents import LlmAgent
from sf_mcp_proxy.integrations.adk import SalesforceToolset
toolset = SalesforceToolset(server="sobject-reads", env="sandbox")
agent = LlmAgent(model="gemini-2.5-pro", name="sf", tools=[toolset])
...
await toolset.close()Tier B — salesforce_stdio_connection_params(...) returns
StdioConnectionParams for ADK's own McpToolset.
Runnable examples are in examples/.
6. Proxy mode
sf-mcp-proxy proxy re-exposes one Salesforce MCP server as a plain MCP
server. The proxy owns the Salesforce OAuth (the token cached by login), so
downstream clients need no Salesforce auth of their own. Every request runs as
the Salesforce user who authorized the proxy (one identity per proxy process).
stdio — for clients that spawn a subprocess:
{
"mcpServers": {
"salesforce-sobject-reads": {
"command": "sf-mcp-proxy",
"args": ["proxy", "-s", "sobject-reads", "-e", "sandbox", "--no-login"]
}
}
}Streamable HTTP — for clients that take a URL:
sf-mcp-proxy proxy -s sobject-reads -e sandbox --no-login \
--http --port 9000 --auth-token "$(openssl rand -hex 16)"
# → endpoint: http://127.0.0.1:9000/mcp (send: Authorization: Bearer <token>)--host/--port/--public-url/--auth-token fall back to
$HOST/$PORT/$PUBLIC_URL/$SF_PROXY_AUTH_TOKEN. For a remote client, bind
--host 0.0.0.0 and put a TLS reverse proxy in front (the proxy speaks plain
HTTP), and always set --auth-token or --oauth.
--oauth
Runs a minimal, spec-shaped OAuth 2.0 Authorization Server + Resource Server on
the same port: RFC 9728 / RFC 8414 discovery, RFC 7591 Dynamic Client
Registration (POST /register), Authorization Code + PKCE (S256), and an
Approve/Deny page on every /authorize request. A spec-compliant client
discovers everything from the 401 it gets hitting /mcp. Manage clients with
sf-mcp-proxy oauth-clients -s … [--revoke <id>]. Behind a reverse proxy, pass
--public-url https://your.host so the discovery documents advertise the right
address. This layer only gates who reaches the proxy — every approved client
still acts as the one identity that ran login.
Docker
A Dockerfile runs proxy --http --no-login and reads $HOST,
$PORT, $PUBLIC_URL, $SF_PROXY_AUTH_TOKEN from the environment.
docker build -t sf-mcp-proxy .
docker run --rm -p 8080:8080 \
-e SF_CONSUMER_KEY=... -e SF_PROXY_AUTH_TOKEN=... -e SF_TOKEN_PASSPHRASE=... \
-v ~/.sf-mcp-proxy:/home/app/.sf-mcp-proxy \
sf-mcp-proxy -s sobject-reads -e sandboxThere's no browser in the container, so --no-login is on: run sf-mcp-proxy login once on your machine and make the token cache available to the
container. On a stateless host (Cloud Run, etc.) mount a persistent volume at
/home/app/.sf-mcp-proxy (the Salesforce refresh token rotates and is written
back), set SF_TOKEN_PASSPHRASE from a secret, and pin --max-instances 1 --min-instances 1 (a Streamable HTTP session is held in memory by the instance
that created it).
7. Chat mode (sf-mcp-proxy[chat])
sf-mcp-proxy chat -s sobject-reads
you> how many open opportunities are closing this quarter?
⚙ soqlQuery({"soql":"SELECT COUNT() FROM Opportunity WHERE IsClosed = false AND ..."})
run soqlQuery? [y/N] yTool calls whose name looks like a write (create/update/delete/run/…)
prompt for confirmation. -y approves everything; --confirm-all prompts for
every call. In-chat commands: /tools, /reset, /quit.
| Base URL | API key env | Default model |
| — |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| — (set |
|
|
| — (set |
SFCHAT_API_KEY works as the key for any provider; SFCHAT_MODEL /
SFCHAT_BASE_URL persist per-run overrides. The model must support tool calling.
How auth works
First request to the MCP server returns
401. The SDK discovershttps://api.salesforce.com/.well-known/oauth-protected-resource/…, which names the org's authorization server.It builds the authorize URL with PKCE (
S256), themcp_api refresh_tokenscopes, andresource=<server URL>(RFC 8707), then opens the browser.Salesforce redirects to
http://localhost:<port>/callbackwith acode; a one-shot loopback server catches it and the SDK exchanges it for tokens.Tokens are written to
~/.sf-mcp-proxy/<sha256(url)[:32]>.json(dir0700, files0600; encrypted ifSF_TOKEN_PASSPHRASEis set). Later runs reuse and silently refresh the access token.
SF_AUTH_SERVER_URL (a My Domain host) bypasses discovery and uses
{base}/services/oauth2/{authorize,token,revoke} directly.
Troubleshooting
Symptom | Fix |
| The ECA Callback URL must byte-for-byte equal |
| Wait ~30 min for propagation. |
Browser shows the wrong org / login | Set |
| Change |
| Run |
Stuck / bad token |
|
Development
uv sync --all-extras --group dev
uv run pytest -q
uv run ruff check . && uv run ruff format --check .
uv run mypy sf_mcp_proxyLicense
MIT © Ocky Harliansyah — see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Authenticated MCP server for ClearPolicy policy and compliance workflows.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
Model Context Protocol server for the Apideck Unified API. Connect any MCP-compatible agent framework to 100+ accounting systems, HRIS platforms, file storage providers, and more through one integration. More information https://www.apideck.com/mcp-server
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceMCP server for Salesforce that exposes CLI, REST, Connect, Data 360, Bulk 2.0, and Einstein Models APIs as tools for any MCP-compatible client to manage orgs, data, and metadata.Apache 2.0
- AlicenseNot gradedqualityCmaintenanceA Model Context Protocol server for interacting with Salesforce Commerce Cloud (SFCC) APIs.6 npm12MIT
- AlicenseNot gradedqualityDmaintenanceA Model Context Protocol server that connects AI assistants to Salesforce orgs, enabling querying, searching, creating, updating, and managing Salesforce data through natural language via any MCP-compatible client.29 npmMIT
- FlicenseCqualityCmaintenanceA Model Context Protocol server for Salesforce development workflows, enabling org lookup, metadata deploy/retrieve, SOQL queries, Apex tests, and permission set assignment.72-