Skip to main content
Glama
TechwithZakir

Salesforce Headless MCP AI

README.md
# Salesforce Headless MCP AI

A production-oriented Frappe v15+ application that gives each authorized Frappe user a web AI assistant backed by a configurable AI provider and Salesforce Hosted MCP. Salesforce OAuth tokens and AI API keys remain encrypted on the Frappe server; browser code calls only whitelisted Frappe methods.

> Salesforce Headless 360 MCP is currently a Salesforce Beta service. Validate availability, limits, and terms for your org before production use.
<img width="1631" height="966" alt="image" src="https://github.com/user-attachments/assets/94650778-bb24-4d66-a742-ecd6887705fd" />

## Project documentation

- [Development technical documentation](docs/DEVELOPMENT_TECHNICAL_DOCUMENTATION.md)
- [Complete project presentation script](docs/PROJECT_PRESENTATION_SCRIPT.md)

## Architecture

```text
Frappe browser page (/salesforce-ai)
        ↓ authenticated frappe.call
Frappe Python services
        ├── OpenAI / Claude / Gemini / Azure / local LLM (tool calling)
        └── Salesforce Hosted MCP (Streamable HTTP + OAuth bearer token)
                    ↓
              Salesforce org
```

The MCP tool list is retrieved at runtime. In read-only mode the server permits only `discover`, `describe`, and `dispatch_readonly`; `dispatch` is removed from the model's tools and independently blocked in Python. When writes are enabled, `dispatch` is held in a ten-minute, user-bound confirmation record until the user explicitly confirms.

The app also supports Salesforce SObject servers. For `platform/sobject-all`, read-only mode exposes only tools marked read-only (plus Salesforce's known schema/query/search/identity/relationship read tools). When writes are enabled, discovered mutation tools are exposed and every create/update/delete call uses the same confirmation gate.

## Requirements

- Frappe Framework v15 or newer and a working Bench site
- Python 3.10+
- A Salesforce org with API v67.0+ and Headless 360 activated
- A Salesforce External Client App configured for OAuth Authorization Code + PKCE S256
- An OpenAI API key with access to the configured model
- HTTPS for the public Frappe site

Python dependencies (`httpx` and `openai`) are declared in `pyproject.toml` and install with the app.

## Installation

```bash
cd ~/frappe-bench
bench get-app https://github.com/TechwithZakir/salesforce_mcp_ai
bench --site yoursite.example install-app salesforce_mcp_ai
bench --site yoursite.example migrate
bench build --app salesforce_mcp_ai
bench restart
```

For local development:

```bash
cd ~/frappe-bench
bench new-app salesforce_mcp_ai
# Replace the generated app with this repository, or clone it into apps/salesforce_mcp_ai.
bench --site development.localhost install-app salesforce_mcp_ai
bench --site development.localhost migrate
bench build --app salesforce_mcp_ai
```

Assign `Salesforce MCP Manager` to administrators and `Salesforce MCP User` to workshop users. Open **Salesforce MCP Settings** as a manager and save the configuration, then visit `/salesforce-ai`.

After upgrading an existing installation, run `bench --site yoursite.example migrate` and `bench --site yoursite.example clear-cache`. The public **Salesforce MCP AI** workspace then appears in Desk for users assigned `Salesforce MCP User`, `Salesforce MCP Manager`, or `System Manager`.

## Salesforce setup

1. In Salesforce Setup, search for **MCP Servers**.
2. Select **MCP Servers**, find **headless-360**, and activate it.
3. Go to **External Client App Manager** and create an External Client App.
4. Enable OAuth Authorization Code flow and require PKCE using S256.
5. Add scopes `mcp_api`, `refresh_token`, and `offline_access` (use the labels/options exposed by your Salesforce release).
6. Set the callback URL exactly to:

```text
https://YOUR-FRAPPE-DOMAIN/api/method/salesforce_mcp_ai.api.auth.salesforce_callback
```

7. Copy the consumer key and, when required by the app policy, client secret into **Salesforce MCP Settings**.

Salesforce enforces the connected user's CRUD, field-level security, sharing, profiles, and permission sets for every MCP operation. Give workshop users only the Salesforce permissions they require.

## Configuration

Recommended production configuration:

```text
Enabled:                         On
AI Provider:                     OpenAI
API Key:                         (encrypted)
Model:                           gpt-5.6
Temperature:                     0.2
Max Tokens:                      4000
Salesforce Login URL:            https://login.salesforce.com
Salesforce MCP URL:              https://api.salesforce.com/platform/mcp/v1/platform/headless-360
Allow Write Operations:          Off
Require Write Confirmation:      On
Enable MCP Trace:                On
Enable Audit Logging:            On
Maximum MCP Tool Calls:          6
Request Timeout:                 60
```

The OpenAI API key and External Client App client secret are Frappe `Password` fields. Access and refresh tokens are also `Password` fields on the per-user **Salesforce Connection** document. They are read only by backend code using Frappe's encrypted-password API.

### Sandbox

Switch without code changes:

```text
Salesforce Login URL: https://test.salesforce.com
Salesforce MCP URL:   https://api.salesforce.com/platform/mcp/v1/sandbox/platform/headless-360
```

Use a sandbox External Client App with the same callback and scopes.

### SObject All server

Activate **SObject All** under Salesforce Setup → MCP Servers, then configure:

```text
Production/Developer org: https://api.salesforce.com/platform/mcp/v1/platform/sobject-all
Sandbox/Scratch org:      https://api.salesforce.com/platform/mcp/v1/sandbox/platform/sobject-all
```

Use read-only mode for querying Cases, Opportunities, Accounts, and other records. Turn writes on only when create/update/delete operations are required; confirmation should normally remain enabled.

## AI provider configuration

Choose one provider in **Salesforce MCP Settings**:

- **OpenAI**: enter an OpenAI API key and model.
- **Claude**: enter an Anthropic API key and Claude model name.
- **Gemini**: enter a Gemini API key and model name.
- **Azure OpenAI**: enter the key, HTTPS resource endpoint, and deployment name. The current v1 endpoint is used.
- **Local LLM (OpenAI-compatible)**: enter an OpenAI-compatible `/v1` base URL and model name. The key may be blank for an unsecured loopback server.

Examples for local servers:

```text
Ollama:    http://127.0.0.1:11434/v1
LM Studio: http://127.0.0.1:1234/v1
LiteLLM:   https://llm.example.com/v1
vLLM:      https://vllm.example.com/v1
```

The selected model must support tool/function calling. Local HTTP is accepted only for a loopback hostname or address; remote endpoints require HTTPS. The agent sends fixed safety instructions and only dynamically returned, policy-filtered MCP schemas. The maximum is counted across individual tool calls, not merely iterations.

## Security and production notes

- Keep **Allow Write Operations** off unless mutations are required. Leave confirmation enabled when writes are on.
- OAuth state and PKCE verifier are server-cache entries, expire after ten minutes, and are consumed once.
- Pending writes are user-bound, single-use, and expire after ten minutes.
- The configured MCP host is restricted to HTTPS on `api.salesforce.com` to reduce SSRF risk.
- Authorization errors trigger exactly one refresh attempt. Failed refreshes disconnect the account.
- Audit payloads are redacted and truncated. Do not add secret fields to traces or custom logs.
- Serve Frappe only over HTTPS, secure Redis and the encryption key, rotate secrets, use restrictive roles, and configure backups.
- Review Salesforce Beta terms and your organization's data-handling policy before sending Salesforce data to an LLM.
- Normal chat is synchronous for workshop clarity. Service classes are isolated so a later endpoint can invoke them through `frappe.enqueue`.

## Permissions

- **Salesforce MCP User**: use the assistant, connect/disconnect their own Salesforce account, and view only their own logs.
- **Salesforce MCP Manager**: configure settings and view all audit logs/connections. Secret values are never returned by an API.

Ownership is enforced by DocType permission hooks as well as filters in each whitelisted method.

## Tests and verification

Tests use mocks and do not require a live Salesforce org or OpenAI call:

```bash
bench --site development.localhost run-tests --app salesforce_mcp_ai
bench --site development.localhost migrate
bench build --app salesforce_mcp_ai
```

Coverage includes PKCE, one-time OAuth state, token refresh, read-only tool filtering and blocking, connection/log ownership, and the tool-call limit setting. For a release, also perform a browser smoke test of `/salesforce-ai`, OAuth callback, tool discovery, and one read-only prompt against a dedicated Salesforce test user.

## Workshop demo

1. Sign in as a user with the `Salesforce MCP User` role.
2. Open `/salesforce-ai`, click **Connect Salesforce**, and complete OAuth.
3. Confirm that the badge is green, mode says **Read Only**, and the MCP tools appear.
4. Ask: “Find Acme Corporation.” Expand the trace to show `discover`, `describe`, and `dispatch_readonly`.
5. Ask: “Show its open opportunities,” then “Which opportunity should I prioritize?”
6. Optionally enable writes as a manager. Ask to create a follow-up task, review the proposed `dispatch` arguments, and click **Confirm & Execute**.
7. Open **Salesforce MCP Log** to show the truncated audit trail.

## Troubleshooting

- **OAuth state invalid or expired**: restart Connect Salesforce and finish within ten minutes; verify Redis/cache health and that the callback returns to the same Frappe deployment.
- **redirect_uri mismatch**: copy the callback shown above exactly, including scheme and hostname.
- **invalid scope / MCP access denied**: activate Headless 360, confirm API v67.0+, the `mcp_api` scope, and the user's Salesforce permissions.
- **401 after connection**: reconnect if refresh failed; verify the External Client App refresh-token policy and client secret requirement.
- **No MCP tools**: confirm the configured production/sandbox endpoint and server activation.
- **OpenAI error**: verify the API key, model access, quota, outbound network access, and timeout.
- **Maximum tool calls reached**: make the prompt more specific or cautiously raise the limit (maximum accepted by this app is 20).
- **Page redirects to login / permission denied**: sign in and assign one of the Salesforce MCP roles.

Technical failures are recorded with `frappe.log_error`; browser responses do not include stack traces or secrets.

## Source layout

```text
salesforce_mcp_ai/
├── api/          # whitelisted auth, MCP, and agent methods
├── services/     # OAuth, token, MCP transport, OpenAI orchestration
├── utils/        # PKCE, validation, redaction
├── salesforce_mcp_ai/doctype/
├── tests/
└── www/salesforce-ai/
```

## License

MIT