Developer guides
Deploy the MCP service one security gate at a time
Detailed page. Use “On this page” to jump directly to the section you need.
Use this page when deploying and securing the hosted MCP service. This is not a Lightning or Flow administrator task. If you cannot run Node.js and container security checks, hand this page to the team that operates hosted services.
Use this guide to deploy the separately hosted Record Health Check MCP service and prove that every security gate works. The service gives an approved AI client two read-only tools:
run_record_health_checkevaluates one exact Check for one Salesforce record.run_record_health_check_setevaluates one exact Check Set for one Salesforce record.
This is an implementation guide, not only a security reference. Complete the gates in order. At each gate, run both the successful test and the rejection test before continuing.
The MCP service uses one dedicated Salesforce integration user. Results reflect that user’s object, field, record-sharing, restriction-rule, and scoping-rule access. It does not use the access of the person chatting with the AI client. Use native Agentforce actions when results must reflect the Agentforce principal instead.
What you will build
Section titled “What you will build”Approved AI client -> HTTPS /mcp endpoint -> host and Origin checks -> JWT signature and claim checks -> two approved tools and request validation -> concurrency, timeout, retry, and kill-switch controls -> Salesforce OAuth client-credentials connection -> packaged read-only Apex REST endpoint -> sharing, object, field, and record access of the integration userThe gates are cumulative. Passing an outer gate never bypasses a later gate.
| Gate | What it proves |
|---|---|
| 1 | Only approved HTTP hosts and browser origins reach the service. |
| 2 | Production uses HTTPS and cannot disable authentication. |
| 3 | The inbound bearer token was signed by the trusted identity provider. |
| 4 | The token was issued for this service, is current, identifies a subject, and has rhc.run. |
| 5 | The MCP client can discover only the two intended tools. |
| 6 | A tool call contains one safe record ID and one exact Qualified API Name. |
| 7 | Load, retries, timeouts, response size, and emergency shutdown are bounded. |
| 8 | The service can call only approved HTTPS Salesforce hosts. |
| 9 | Salesforce authenticates the dedicated integration user. |
| 10 | The integration user has the package’s run entitlement. |
| 11 | Salesforce data security permits the requested record and fields. |
Before you start
Section titled “Before you start”You need:
- A non-production Salesforce org with Record Health Check installed.
- An active Check Set and at least one Check that you can test safely.
- The exact Check and Check Set Qualified API Names copied from Salesforce Setup. Do not add or
remove
rhc__yourself. - One readable test record and one record that the future integration user cannot read.
- Permission to create a Salesforce user, Permission Sets, and an External Client App or supported Connected App.
- Node.js as specified in the toolchain configuration, Docker when the hosting platform uses containers, and a secret manager.
- A hosting platform that terminates HTTPS and can set environment variables and secrets.
- An OAuth 2.0 identity provider that issues JWT access tokens and publishes a JWKS endpoint.
- An MCP client that supports Streamable HTTP and bearer authentication.
The inbound JWT scope rhc.run belongs to the MCP service’s identity provider. Salesforce’s
Record Health Check Run Custom Permission is a separate authorization gate on the Salesforce
integration user. A request must pass both.
Record these non-secret values before proceeding:
| Name used below | Example | Where it comes from |
|---|---|---|
| MCP host | mcp.example.com | DNS and hosting configuration |
| MCP endpoint | https://mcp.example.com/mcp | MCP host plus /mcp |
| issuer | https://identity.example.com | Inbound identity provider |
| audience | record-health-check | Inbound OAuth resource configuration |
| JWKS URL | https://identity.example.com/.well-known/jwks.json | Identity provider |
| Salesforce login URL | https://example.my.salesforce.com | The org’s My Domain URL |
| Salesforce username | record-health-mcp@example.com | Dedicated integration user |
| readable record ID | A 15- or 18-character ID | Test record visible to the integration user |
| denied record ID | A 15- or 18-character ID | Test record hidden from the integration user |
| Check Set name | My_Account_Checks | Exact QualifiedApiName from Setup |
Never put client secrets, access tokens, session IDs, or production record IDs in this worksheet, source control, screenshots, tickets, or command history.
Step 1: Prepare and verify the service locally
Section titled “Step 1: Prepare and verify the service locally”This step proves the source and tests before credentials are introduced.
-
Open a terminal in
packages/record-health-check-mcp. -
Confirm that
node --versionmatches the major version in the toolchain configuration. -
Install the locked dependencies:
Terminal window npm ci -
Run the complete MCP package check:
Terminal window npm run check -
Confirm that formatting, linting, type checking, unit tests, coverage, protocol tests, and build all pass.
-
Do not copy
.env.exampleinto source control. Use it only as a list of required fields.
If this step fails, fix the local build before configuring Salesforce or the hosting platform. A successful local test does not prove any production gate, but a failed local test makes later gate results unreliable.
Step 2: Create the dedicated Salesforce integration user
Section titled “Step 2: Create the dedicated Salesforce integration user”Do not use an administrator, a human employee, or the Agentforce user.
- In Salesforce Setup, enter
Usersin Quick Find, then select Users. - Select New User.
- Enter a descriptive name such as
Record Health MCP Integration. - Enter a unique username controlled by the integration owner.
- Select the least-privileged API-capable license available for the required objects and Apex REST access in your org.
- Select a minimal profile. Do not select System Administrator.
- Save the user.
- In Setup, enter
Permission Setsin Quick Find, then select Permission Sets. - Open Record Health Check MCP Integration.
- Select Manage Assignments, then Add Assignments.
- Select the dedicated integration user and complete the assignment.
- Do not assign Record Health Check Admin merely to make the integration work.
If operators require restricted package-error telemetry for this principal, separately assign
Record Health Check Error Log Publisher and route Record_Health_Check_Log__e only to an
approved restricted monitoring channel. Without that optional assignment, evaluation responses
remain safe but the principal cannot publish the package’s restricted Log Platform Event. Do not
grant this permission merely to make ordinary MCP requests work.
Expected result: the user has only the versioned REST entry point, the package run permission, and read access to the two Custom Metadata types. It has no package UI, Flow, Agentforce, async, lifecycle-event, administrator, diagnostic-detail, or restricted error-log access unless the separate optional publisher Permission Set is deliberately assigned.
Step 3: Grant only the required Salesforce data access
Section titled “Step 3: Grant only the required Salesforce data access”The package Permission Set grants package access. It cannot know which business objects and fields your Checks use.
- Create a separate Permission Set such as
Record Health MCP Data Access. - In that Permission Set, open Object Settings.
- For each object evaluated by the selected Check Sets, grant Read only.
- Grant field read access only for fields used by those Checks.
- Grant access to parent or related objects and fields used by formulas or queries.
- Do not grant Create, Edit, Delete, Modify All, or View All unless a separately approved requirement needs it. The MCP tools themselves are read-only.
- Assign the Permission Set to the dedicated integration user.
- Configure sharing so the user can read the intended test record.
- Keep a second test record outside the user’s sharing access.
- If the org uses restriction rules or scoping rules, confirm that they produce the intended record set for this user.
Expected result: the user can read every object and field required for the approved Checks, but cannot read the denied test record or unrelated sensitive fields.
Step 4: Create the Salesforce OAuth client-credentials connection
Section titled “Step 4: Create the Salesforce OAuth client-credentials connection”Salesforce calls this a client credentials flow. It authenticates one configured integration user without an interactive login.
- In Setup, enter
External Client Apps Managerin Quick Find, then select External Client Apps Manager. - Create an External Client App for the Record Health Check MCP service. If your org uses a supported Connected App instead, apply the equivalent client-credentials policy.
- Give the app a specific name such as
Record Health Check MCP Production. - Enable OAuth.
- Add only the OAuth scopes needed to call the packaged Apex REST endpoint. Avoid broad scopes that the service does not require.
- Enable Client Credentials Flow.
- Set the run-as user to the dedicated integration user created in Step 2.
- Save the app and allow time for Salesforce configuration propagation.
- Obtain the consumer key and secret using your org’s protected consumer-details workflow.
- Store them immediately in the hosting secret manager as
SALESFORCE_CLIENT_IDandSALESFORCE_CLIENT_SECRET. - Do not paste either value into
.env.exampleor a deployment manifest.
Expected result: the app is bound to the dedicated user, and the secret manager contains the credentials. The source repository does not contain them.
Step 5: Build and deploy an immutable service image
Section titled “Step 5: Build and deploy an immutable service image”-
From
packages/record-health-check-mcp, build the image:Terminal window docker build -t record-health-check-mcp:local . -
Scan the dependencies and the final image with your approved scanners.
-
Generate an SBOM.
-
Push the image to the approved registry.
-
Record the immutable image digest.
-
Configure the hosting platform to deploy by digest, not a mutable tag.
-
Configure HTTPS, DNS, and the
/mcproute. -
Run the container as its built-in unprivileged Node user. Do not override it to root.
-
Set
BUILD_IDto the source revision or other immutable release identifier.
Do not enable public production traffic yet. The remaining steps configure and prove every gate.
Gate 1: Approved HTTP hosts and Origins
Section titled “Gate 1: Approved HTTP hosts and Origins”This gate prevents requests sent through an unexpected hostname or browser origin.
Configure the gate
Section titled “Configure the gate”- Set
ALLOWED_HOSTSto the exact public MCP hostname. For multiple hosts, use a comma-separated list with no wildcards. - Set
ALLOWED_ORIGINSto each approved browser origin host. If no browser client is approved, retain only the explicitly required non-browser behavior established by your platform tests. - Set
MCP_SERVER_URLto the public HTTPS URL ending in/mcp. - Confirm that the load balancer preserves the original
Hostvalue expected by the application. - Restart the service through the hosting platform’s normal deployment mechanism.
Example non-secret values:
ALLOWED_HOSTS=mcp.example.comALLOWED_ORIGINS=approved-client.example.comMCP_SERVER_URL=https://mcp.example.com/mcpProve the gate
Section titled “Prove the gate”- Send a request through the approved public hostname. Authentication may reject it at a later gate, but the response must not be a host rejection.
- Repeat with a deliberately unapproved
Hostvalue in a controlled test environment. - Repeat with an unapproved
Originheader. - Confirm that both negative requests are rejected before Salesforce is called.
- Confirm that logs contain only the safe error category. They must not contain request headers or tokens.
If approved traffic fails, compare the externally visible hostname with ALLOWED_HOSTS and inspect
the reverse proxy’s host-forwarding configuration.
Gate 2: HTTPS and production authentication mode
Section titled “Gate 2: HTTPS and production authentication mode”This gate prevents plaintext production destinations and unauthenticated production startup.
Configure the gate
Section titled “Configure the gate”- Set
NODE_ENV=production. - Set
AUTH_MODE=jwt. - Set
MCP_SERVER_URLto anhttps://URL ending in/mcp. - Set
SALESFORCE_LOGIN_URLto the org’s HTTPS My Domain URL. - Configure the hosting platform to redirect or reject plaintext HTTP before it reaches the app.
- Confirm that TLS certificate renewal and expiry monitoring are enabled.
Prove the gate
Section titled “Prove the gate”- Start the production configuration with
AUTH_MODE=jwt; it must start when all required JWT fields are present. - In a disposable non-production revision, set
AUTH_MODE=nonewhile leavingNODE_ENV=production. - Confirm that configuration validation prevents startup.
- Restore
AUTH_MODE=jwt. - Request the HTTP URL and confirm that the platform redirects to HTTPS or rejects it.
- Request
/.well-known/oauth-protected-resource/mcpthrough the public HTTPS host and confirm it returns the exact MCP resource URL, configured authorization-server issuer, andrhc.runscope. - POST to
/mcpwithout a token and confirm the401WWW-Authenticatechallenge contains the protected-resource metadata URL and required scope. - Send an authenticated
GET /mcpwithAccept: text/event-streamand confirm the stateless service returns405 Method Not AllowedwithAllow: POST, rather than404or an accidental legacy-transport fallback.
Never use AUTH_MODE=none to diagnose production authentication. It is intended only for bounded
local development outside production.
Gate 3: JWT signature verification
Section titled “Gate 3: JWT signature verification”This gate proves that the inbound access token was signed by the trusted identity provider.
Configure the gate
Section titled “Configure the gate”- Register the MCP service as an OAuth resource or API in the inbound identity provider.
- Configure the provider to issue JWT access tokens signed with RS256 or ES256.
- Copy the provider’s HTTPS JWKS URL.
- Set
MCP_AUTH_JWKS_URLto that URL. - Ensure the service can reach the JWKS host through approved outbound network policy.
- Establish a key-rotation procedure that overlaps old and new public keys long enough for current tokens to expire safely.
Prove the gate
Section titled “Prove the gate”- Obtain a short-lived access token through the approved client flow.
- Call
tools/listwith that bearer token and confirm that authentication succeeds. - Change one character in the token signature and repeat the request.
- Confirm that the altered token is rejected and Salesforce is not called.
- Test a token signed by an unrelated key and confirm rejection.
- Rotate a non-production signing key and confirm that expected overlap and retirement behavior match the documented procedure.
Do not decode or print production tokens in logs. Use synthetic non-production tokens for negative tests.
Gate 4: JWT claim verification
Section titled “Gate 4: JWT claim verification”Signature verification alone does not prove that a token belongs to this service.
Configure the gate
Section titled “Configure the gate”- Set
MCP_AUTH_ISSUERto the token’s exactissvalue, including scheme and path. - Set
MCP_AUTH_AUDIENCEto the audience assigned to this MCP service. - Set
MCP_AUTH_REQUIRED_SCOPE=rhc.run. - Configure the identity provider to include a nonempty subject (
sub). - Configure short token expiration and approved clock synchronization for the service hosts.
- Grant
rhc.runonly to approved MCP clients.
Prove the gate
Section titled “Prove the gate”Obtain synthetic tokens that vary one claim at a time and verify these results:
| Token | Expected result |
|---|---|
Correct issuer, audience, subject, expiry, and rhc.run | Accepted |
| Wrong issuer | Rejected |
| Wrong audience | Rejected |
| Missing subject | Rejected |
| Expired token | Rejected |
Missing rhc.run | Rejected |
rhc.run present among other space-separated scopes | Accepted |
Confirm that every rejected request stops before a Salesforce token request or Apex REST call.
Gate 5: Approved MCP capabilities
Section titled “Gate 5: Approved MCP capabilities”This gate limits what an authenticated AI client can discover and invoke.
Configure the gate
Section titled “Configure the gate”No environment setting broadens the approved tool list. The deployed source defines it. Do not add generic SOQL, arbitrary Apex, record mutation, MCP resources, or MCP prompts to this service.
Prove the gate
Section titled “Prove the gate”- Connect an official or conforming MCP SDK client to the
/mcpendpoint. - Authenticate with a valid token.
- Send
tools/list. - Confirm that the response contains exactly:
run_record_health_checkrun_record_health_check_set
- Confirm that
resources/listandprompts/listdo not expose Salesforce content. - Attempt to call an invented tool such as
run_soql. - Confirm that the call is rejected before Salesforce is contacted.
- Save the redacted tool list so you can compare it after a service update.
Repeat this proof after every dependency or tool-contract change.
Gate 6: Tool request contract
Section titled “Gate 6: Tool request contract”This gate prevents the model or client from sending ambiguous, excessive, or unsafe arguments.
Configure a first call
Section titled “Configure a first call”-
Copy the exact Check Set Qualified API Name from Salesforce Setup.
-
Copy the readable test record’s 15- or 18-character Salesforce ID.
-
Choose a safe correlation ID containing only letters, numbers,
.,_,:, or-. -
Call
run_record_health_check_setthrough the MCP client with these logical arguments:{"recordId": "001000000000001AAA","qualifiedApiName": "My_Account_Checks","correlationId": "mcp-guide-pass-001"} -
Replace the example values. Do not send the literal example record ID.
-
Confirm that the response uses contract version
1.0and returns a health status or a safe adapter error.
Prove the gate
Section titled “Prove the gate”Test each invalid request separately:
- Missing record ID.
- Record ID with the wrong length or characters.
- Missing Qualified API Name.
- Qualified API Name containing spaces or punctuation that Salesforce names cannot contain.
- An extra, unknown argument.
- An oversized request body.
- A correlation ID containing newline characters or other unsafe punctuation.
Every case must be rejected safely without echoing the complete arguments into logs. An unknown but syntactically valid Qualified API Name can pass this outer contract and be rejected later by Salesforce; that distinction is expected.
Gate 7: Operational containment
Section titled “Gate 7: Operational containment”This gate keeps valid-looking traffic from exhausting the service or Salesforce.
Configure the gate
Section titled “Configure the gate”Start with the repository defaults unless load tests justify a lower value:
SALESFORCE_TIMEOUT_MS=10000SALESFORCE_MAX_RESPONSE_BYTES=65536SALESFORCE_MAX_RETRIES=1MAX_CONCURRENT_SALESFORCE_CALLS=10KILL_SWITCH=false- Set the hosting platform’s request and autoscaling limits at or below approved capacity.
- Keep
SALESFORCE_MAX_RETRIESlow. Retrying multiplies Salesforce traffic during an outage. - Keep the service response limit aligned with the package’s bounded contract.
- Restrict permission to change
KILL_SWITCHto named operators. - Create an alert for concurrency rejection, timeout, response-size rejection, retries, and kill-switch activation.
Prove the gate
Section titled “Prove the gate”- Send fewer simultaneous requests than
MAX_CONCURRENT_SALESFORCE_CALLS; they should proceed. - Exceed that number in staging; excess work must fail predictably rather than queue without bound.
- Make the Salesforce test double respond slower than
SALESFORCE_TIMEOUT_MS; confirm timeout. - Return a response larger than
SALESFORCE_MAX_RESPONSE_BYTES; confirm rejection. - Simulate a transient Salesforce response and confirm no more than the configured retries occur.
- Set
KILL_SWITCH=truein staging. - Call both tools and confirm unavailable responses.
- Confirm the Salesforce mock or audit counter does not increase.
- Set
KILL_SWITCH=falseand repeat one successful smoke test.
The kill switch is the fastest containment control. It is not a substitute for revoking compromised inbound or Salesforce credentials.
Gate 8: Approved Salesforce destinations
Section titled “Gate 8: Approved Salesforce destinations”This gate prevents access tokens and requests from being redirected to an unapproved host.
Configure the gate
Section titled “Configure the gate”- Set
SALESFORCE_LOGIN_URLto the org’s exact HTTPS My Domain login URL. - Set
SALESFORCE_ALLOWED_HOSTSto that hostname. - Perform a non-production token request and note the exact Salesforce instance hostname returned.
- Add every legitimate returned instance hostname to
SALESFORCE_ALLOWED_HOSTS. - Use comma-separated hostnames only. Do not include a scheme, path, port, or wildcard.
- Review the list after a Salesforce instance migration or My Domain change.
Example:
SALESFORCE_LOGIN_URL=https://example.my.salesforce.comSALESFORCE_ALLOWED_HOSTS=example.my.salesforce.com,example.my.salesforce-sites.comOnly list a host that the actual OAuth and Apex REST flow requires. The second example host is not a universal requirement.
Prove the gate
Section titled “Prove the gate”- Run a valid call and confirm both the login and instance hosts are approved.
- In staging, remove the returned instance host and repeat the call.
- Confirm safe rejection before sending the Apex REST request to that host.
- Simulate an OAuth or Salesforce redirect to an unapproved host.
- Confirm that redirects are rejected rather than followed.
- Restore the approved host list and repeat the successful call.
Gate 9: Salesforce authentication
Section titled “Gate 9: Salesforce authentication”This gate proves that Salesforce recognizes the OAuth app and executes as the dedicated user.
Configure the gate
Section titled “Configure the gate”-
Inject
SALESFORCE_CLIENT_IDandSALESFORCE_CLIENT_SECRETfrom the secret manager. -
Set
SALESFORCE_REST_PATHto the packaged endpoint:/services/apexrest/rhc/record-health-check/contract-1/evaluations -
Restrict secret access to the service runtime and credential owners.
-
Deploy a configuration revision without printing environment values.
-
Use Salesforce login history and External Client App audit information to confirm that calls run as the dedicated integration username.
Prove the gate
Section titled “Prove the gate”- Call one tool with valid inbound authentication and valid Salesforce credentials.
- Confirm the executing Salesforce username in protected audit data.
- Replace the Salesforce secret with a synthetic invalid value in staging.
- Confirm safe authentication failure and no tool result.
- Restore the valid secret through the secret manager.
- Repeat the successful call.
- Rotate the secret using an overlap procedure, then revoke the old credential and prove that it no longer works.
Never troubleshoot this gate by assigning System Administrator to the integration user.
Gate 10: Package run entitlement
Section titled “Gate 10: Package run entitlement”This gate proves that an authenticated Salesforce user is allowed to run Record Health Check.
Configure the gate
Section titled “Configure the gate”- In Setup, open Permission Sets.
- Open Record Health Check MCP Integration.
- Confirm that the dedicated integration user appears under Manage Assignments.
- Confirm that the user’s assigned permissions include the packaged run Custom Permission.
- Do not assign Record Health Check Admin or Record Health Check Diagnostics Viewer.
Prove the gate
Section titled “Prove the gate”- Run a known-readable Check Set and confirm the request reaches evaluation.
- Remove Record Health Check MCP Integration from the integration user in a controlled test org.
- Repeat the same call.
- Confirm a safe permission error. It must not return
PASS,FAIL, or a diagnostic stack trace. - Reassign Record Health Check MCP Integration.
- Repeat the successful call.
This test isolates package entitlement from normal object and field access. Keep the same record, Check Set, OAuth app, and inbound token for both calls.
Gate 11: Salesforce object, field, sharing, and rule access
Section titled “Gate 11: Salesforce object, field, sharing, and rule access”This final gate proves that successful authentication and package entitlement do not bypass Salesforce data security.
Prove object access
Section titled “Prove object access”- Run a Check against the readable test record and record the safe result.
- Remove read access to the target object from the integration user’s data Permission Set.
- Repeat the same call and confirm
UNABLE_TO_EVALUATEor the documented safe access error. It must not return a falsePASS. - Restore object read access.
Prove field access
Section titled “Prove field access”- Identify one field required by the test Check.
- Remove read access to only that field.
- Repeat the call and confirm the package does not reveal the value or claim a reliable pass.
- Restore field access.
Prove record access
Section titled “Prove record access”- Run the Check against the readable record.
- Repeat it with the denied record ID.
- Confirm the denied record does not produce its health data.
- Review sharing, restriction rules, and scoping rules if the result differs from the expected denial.
Prove diagnostic separation
Section titled “Prove diagnostic separation”- Trigger a safe configuration or access failure in the test org.
- Confirm that the MCP response omits raw field values, queries, formulas, stack traces, and administrator diagnostics.
- Confirm that the integration user is not assigned Record Health Check Admin or Record Health Check Diagnostics Viewer.
- Restore the test configuration.
Step 6: Connect the approved MCP client
Section titled “Step 6: Connect the approved MCP client”MCP client screens differ, but the values and proof are the same.
- Add a remote Streamable HTTP MCP server in the approved client.
- Enter the exact
MCP_SERVER_URLending in/mcp. - Confirm that the client discovers
/.well-known/oauth-protected-resource/mcp, follows its authorization-server issuer, and uses that issuer’s OAuth or OpenID discovery metadata. - Pre-register the client with the identity provider when required, then request resource/audience
MCP_SERVER_URL/MCP_AUTH_AUDIENCEas required by that provider and scoperhc.run. - Authenticate as an approved client subject.
- Refresh the tool list.
- Confirm that exactly the two Record Health Check tools appear.
- Ask the client to evaluate the known readable record with the exact Check Set Qualified API Name.
- Inspect the interaction details and confirm the selected tool and arguments.
- Confirm that the client describes
FAILas an unhealthy business result, not a tool failure. - Confirm that
UNABLE_TO_EVALUATEandERRORare never translated toPASS.
If the client cannot perform protected-resource and authorization-server discovery or send a bearer token to a remote Streamable HTTP server, it is not compatible with this production deployment as configured.
Step 7: Run the adoption test matrix
Section titled “Step 7: Run the adoption test matrix”Before production, test these cases end to end through the actual client:
| Case | Expected behavior |
|---|---|
Known PASS Check | Client reports that the requirement passed. |
Known FAIL Check | Client reports a business condition requiring attention. |
SKIPPED Check | Client says the Check did not apply or run. |
| Missing required access | Client does not claim a reliable result. |
| Unknown valid Qualified API Name | Safe not-found/configuration response. |
| Malformed Qualified API Name | Rejected at the request-contract gate. |
| Denied record | No record health data is disclosed. |
| Expired inbound token | Rejected before Salesforce. |
Missing rhc.run | Rejected before Salesforce. |
| Invented MCP tool | Rejected before Salesforce. |
| Kill switch enabled | Both tools unavailable; Salesforce call count unchanged. |
| Prompt-like text in record data | Treated as data, not as new instructions. |
| Unrelated user question | Neither health-check tool is selected. |
Retain redacted evidence containing the build ID, test case, expected result, actual result, time, and approver. Do not retain tool arguments or Salesforce data.
Step 8: Promote and operate safely
Section titled “Step 8: Promote and operate safely”- Promote the same image digest through development, test, staging, and production.
- Use separate inbound OAuth clients, Salesforce OAuth apps, Salesforce users, secrets, domains, and approved host lists in every environment.
- Run Gates 1–11 in staging against the exact production candidate.
- Deploy production configuration through review and approval.
- Run only approved non-sensitive smoke tests in production.
- Monitor authentication rejection, latency, Salesforce
429, timeouts, retries, schema mismatch, concurrency rejection, and kill-switch state. - Review access quarterly and after any tool, scope, object, field, identity, or ownership change.
- Rotate inbound and Salesforce credentials using overlap, verification, and old-credential revocation.
- Practice rollback to the previous image digest in staging.
- Practice enabling the kill switch and revoking both trust relationships.
Use the MCP operations and security runbook for incident response, telemetry rules, evidence, rotation, and rollback exercises.
Troubleshooting by gate
Section titled “Troubleshooting by gate”| Symptom | First gate to inspect | What to check |
|---|---|---|
| Request rejected immediately by hostname | 1 | Public hostname, proxy forwarding, ALLOWED_HOSTS |
| Service will not start in production | 2 | HTTPS URLs, AUTH_MODE, missing JWT values, BUILD_ID |
| Every bearer token is invalid | 3 | JWKS reachability, signing algorithm, active key ID |
| Token is signed but rejected | 4 | Exact issuer, audience, subject, expiry, rhc.run |
| Unexpected tools appear | 5 | Deployed source and image digest |
| One tool call is rejected before Salesforce | 6 | Field names, record ID, Qualified API Name, extra fields |
| Requests time out or receive unavailable | 7 | Kill switch, concurrency, timeout, retry and response limits |
OAuth works but the instance call returns VALIDATION and is blocked | 8 | Returned instance hostname in SALESFORCE_ALLOWED_HOSTS; this is destination policy, not an OAuth authorization failure |
| Salesforce returns authentication failure | 9 | Client ID, rotated secret, app policy, run-as user |
| Salesforce user authenticates but cannot run package | 10 | Record Health Check MCP Integration assignment and run permission |
| One record or Check cannot be evaluated | 11 | Object, field, sharing, restriction, and scoping-rule access |
Change one gate at a time during diagnosis. Broadening several approved host lists or permissions at once makes the final security boundary impossible to prove.
Official Salesforce references
Section titled “Official Salesforce references”- Configure a Client Credentials Flow
- External Client Apps
- Create custom Agentforce actions using Apex
- Agentforce actions overview
- MCP solutions for Salesforce developers
Salesforce Setup labels and available licenses can vary by edition and release. If a label differs, use the official reference for the org’s current release and preserve the security outcome described at that step.