Salesforce Managed MCP Server
The Salesforce managed MCP server lets agents work with Salesforce CRM data: running SOQL queries and SOSL searches, reading and writing records, inspecting object metadata, and running saved Salesforce reports through the REST and Analytics APIs.
After reading this page, you will be able to:
-
Choose between service-account OAuth and per-user OAuth for the Salesforce managed MCP server
-
Configure the Salesforce managed MCP server against your org
-
Query, modify, and report on Salesforce records from an agent
What this MCP server does
The Salesforce MCP exposes the following tools:
| Tool | What it does |
|---|---|
|
Execute a SOQL query. |
|
Fetch the next page of a large query result, using the |
|
Execute a SOSL full-text search. |
|
List all available sObject types in the org. |
|
Get field and schema metadata for an sObject. |
|
Fetch a record by ID, with optional field selection. |
|
Create a new record. |
|
Update an existing record (PATCH). |
|
Delete a record. |
|
Get a report’s metadata: columns, groupings, and filters. |
|
Run a saved report synchronously and return its results. |
The run_report tool executes a saved Salesforce report through the Analytics REST API, applying the report’s built-in groupings, aggregates, and filters that raw SOQL cannot reproduce. Use describe_report first to discover a report’s columns and filters. To list reports, use query with SELECT Id, Name, DeveloperName FROM Report.
Choose an authentication mode
The Salesforce MCP supports two authentication modes. Set exactly one in the managed configuration:
| Mode | When to use |
|---|---|
Service Account OAuth ( |
OAuth 2.0 client-credentials grant through a Connected App. Service-to-service: no browser, no redirect. Every caller shares one Salesforce identity. Use this when a single shared identity is acceptable. |
User OAuth ( |
Each caller’s Salesforce OAuth token is resolved from the token vault on every request, so tool calls respect Salesforce row-level security for the calling user. Requires a Salesforce OAuth provider. Use this when each user must act as themselves. |
Prerequisites
Before you create the server, make sure you have:
-
A Salesforce org where you can create a Connected App. A free Developer Edition org or a Sandbox works. Newer orgs might need Salesforce Support to allow Connected App creation.
-
Your Salesforce instance URL (your My Domain URL), such as
https://mycompany.my.salesforce.com. Use the fullhttps://base domain with no path. -
For service-account OAuth: a Connected App configured for the client-credentials flow, and its consumer key and consumer secret.
-
For per-user OAuth: a Salesforce OAuth provider configured in Redpanda Agentic Data Plane. See Configure an OAuth Provider and User-delegated OAuth.
|
Starting with the Spring '26 release, Salesforce disables creating new Connected Apps by default. If your org can’t create one, request the ability from Salesforce Support, or use an External Client App, which Salesforce offers as the successor to Connected Apps. The steps on this page describe a Connected App. |
Create a Connected App for service-account OAuth
Skip this section if you are using per-user OAuth.
The client-credentials flow is a service-to-service flow. A client_id and client_secret are exchanged for an access_token, with no browser interaction.
-
In Salesforce Setup, search for App Manager and click New Connected App.
-
Enter a Connected App Name and Contact Email.
-
Under API (Enable OAuth Settings):
-
Select Enable OAuth Settings.
-
Set a Callback URL (required by the form, unused for client credentials).
-
Add the Manage user data via APIs (api) OAuth scope.
-
Select Enable Client Credentials Flow.
-
-
Click Save, then Continue.
-
Open the app from App Manager, click Manage Consumer Details, and copy the Consumer Key (your
client_id) and Consumer Secret (yourclient_secret). -
Set a Run As user. In App Manager, open the app, click Manage > Edit Policies, and under Client Credentials Flow set Run As to a Salesforce user with API access. Click Save.
Store the consumer secret in the Redpanda Agentic Data Plane secret store under an UPPER_SNAKE_CASE key, such as SALESFORCE_CLIENT_SECRET.
Configure
Create a new Salesforce MCP server in Agentic Data Plane:
-
Open MCP servers in the sidebar.
-
Click Add MCP server.
-
Click the Salesforce card in the marketplace picker.
-
Replace the suggested
Name, and optionally add aDescription. -
In the Salesforce configuration form, set the Salesforce instance URL (
orgUrl) and, underAuth, selectService Account OAuthorUser OAuth. ForUser OAuth, select the Salesforce OAuth provider. -
Optionally pin a Salesforce REST API version. The default is
v65.0. -
If the form shows
Queryable,Enabledis on. Turn it off unless your agents need the two SQL tools. See Expose Salesforce data as SQL tables. -
Click Create server.
The create form turns Enable code mode on by default for this server type. The rpk ai mcp-server create command has no code-mode flag. To add code mode from the CLI, create a Code MCP server named <server-name>-code with --code.mcp-servers <server-name>. See Code Mode.
Configure from the CLI
Use rpk ai to create the server with a managed configuration. Set exactly one authentication variant.
For service-account OAuth, supply the Connected App’s clientId, a secret-store reference for the consumer secret (clientSecretRef), and the token URL:
rpk ai mcp-server create my-salesforce --enabled --managed.config '{
"@type": "type.googleapis.com/redpanda.mcps.salesforce.v1.SalesforceMCPConfig",
"orgUrl": "https://mycompany.my.salesforce.com",
"serviceAccountOauth": {
"clientId": "3MVG9...",
"clientSecretRef": "SALESFORCE_CLIENT_SECRET",
"tokenUrl": "https://mycompany.my.salesforce.com/services/oauth2/token"
}
}'
For per-user OAuth, reference the Salesforce OAuth provider by name. The per-user token is resolved from the token vault on each call:
rpk ai mcp-server create my-salesforce --enabled --managed.config '{
"@type": "type.googleapis.com/redpanda.mcps.salesforce.v1.SalesforceMCPConfig",
"orgUrl": "https://mycompany.my.salesforce.com",
"userOauth": {
"providerName": "salesforce"
}
}'
To pin a specific API version, add "apiVersion": "v62.0" to the configuration. The default is v65.0.
Expose Salesforce data as SQL tables
The Salesforce server can also present standard CRM objects as read-only SQL tables, so an agent can filter records the way it would query a database instead of composing SOQL. SQL exposure is the Enabled toggle under Queryable in the server’s configuration. If SQL tools are available in your organization, the create form shows Queryable with Enabled on, so a server you create there is queryable unless you turn it off. Otherwise the form doesn’t show Queryable, and the server starts with SQL exposure off. Creating a server with rpk ai leaves SQL exposure off unless the managed configuration sets it. An existing server keeps the setting it was saved with. To change it, open the server’s edit form, which shows Queryable if SQL tools are available in your organization or if the server was saved with SQL exposure on. SQL exposure adds two tools:
-
list_tables: Return the table catalog. For each table, the catalog lists its columns and their types, which columns can be filtered and with which operators, whether each filter is exact, the page-size limit, and a schema fingerprint that every query response repeats so a caller can tell when the catalog changed. -
execute_query: Run one query against a table and return a page of rows plus a cursor for the next page.
The execute_query tool reads Salesforce through the server’s configured authentication, so with user-delegated OAuth a query returns only the records the calling user can see, exactly as the other tools do. The list_tables tool returns the catalog, which is the same for every caller and carries no Salesforce data, so a caller who hasn’t connected a Salesforce account yet can still read the catalog and plan a query. Neither tool writes to Salesforce.
The tables are accounts, contacts, leads, opportunities, cases, and users. Each is backed by SOQL over the standard object of the same name and carries a selected set of that object’s standard fields, along with created_date and last_modified_date. Every row has an 18-character id, and you join an owner_id column against users.id.
Only standard objects and their standard fields are cataloged. Custom objects and custom fields never appear as a table or a column, and neither do standard fields outside the catalog or the report tools' results. Reach those with query, run_report, or the other tools on this server.
Because execute_query returns the rows Salesforce sends back without re-checking them against your filters, an agent that needs exact results must re-apply its filters to the rows it receives. The list_tables catalog marks which filters are exact. Four cases are worth designing around:
-
Text comparisons are never exact. SOQL compares strings without regard to case, so an equality,
IN, orLIKEfilter on a text, picklist, email, phone, or URL column can return extra rows. Filters on record IDs behave the same way, because Salesforce also matches the 15-character and 18-character forms of the same ID. -
Long text areas can’t be filtered at all. The catalog reports the
descriptioncolumns onaccounts,opportunities, andcasesbut offers no filter on them, soexecute_queryrefuses a predicate on one. -
Currency amounts can only be tested for null. The server returns
accounts.annual_revenueandopportunities.amountas numbers in each record’s own currency. It doesn’t send a comparison on either column to Salesforce, soIS NULLis the only test you can filter on. -
Timestamp bounds are widened. A SOQL datetime literal carries whole seconds, so the server rounds a bound on
created_date,last_modified_date, or another timestamp column outward, and the query can return rows just outside it.
Date columns such as opportunities.close_date are exact for every operator except !=, which the catalog never marks as exact on any column.
A page holds at most 2000 rows, and an IN list holds at most 100 values. A list longer than the cap fails rather than being silently cut short.
With SQL exposure on, the two tools are available to every caller of the server, including agents. Turn off Enabled on a server whose agents should only reach the curated Salesforce tools.
|
To trim the catalog table by table, hide columns, require filters, retype a column, or serve a table under another name, use the SQL exposure tab on the server’s page. That tab works the same way for every managed type that offers SQL tables. See Narrow what the SQL tools expose. If the server’s page has no SQL exposure tab, narrowing isn’t enabled for your organization. The Enabled setting on the server’s configuration still turns the two SQL tools on and off.
Tool examples
Run a SOQL query:
curl -s https://aigw.<cluster-id>.clusters.rdpa.co/mcp/v1/my-salesforce \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "query",
"arguments": {
"soql": "SELECT Id, Name, Industry, AnnualRevenue FROM Account ORDER BY AnnualRevenue DESC LIMIT 10"
}
}
}'
Fetch a record by ID, restricting the returned fields:
curl -s https://aigw.<cluster-id>.clusters.rdpa.co/mcp/v1/my-salesforce \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_record",
"arguments": {
"sobject": "Account",
"record_id": "001xx000003GYkZAAW",
"fields": "Id,Name,Industry,AnnualRevenue,BillingCity"
}
}
}'
Troubleshooting
| Symptom | What to check |
|---|---|
|
Confirm the Connected App has a Run As user set and the |
|
First call from a user with no stored token. The user completes Salesforce’s OAuth consent flow, the token lands in the vault, and subsequent calls reuse it. |
|
The acting identity (the Run As user for service-account mode, or the calling user for per-user mode) lacks permission on that object or record. Grant the appropriate profile or permission set. |
|
The field does not exist or field-level security hides it from the acting identity. Use |
|
The URL must start with |