sf-api-explorer
by nfunky
README.md
# Salesforce API Explorer
Unofficial Salesforce REST API explorer: generate an OpenAPI document from sObjects and Apex REST, then try requests. Specs stay in your browser.
- [Web app](https://sf-api-explorer.com)
- [MCP server](https://sf-api-explorer.com/mcp) · [how to connect](https://sf-api-explorer.com/mcp-guide)
- [Chrome extension](https://chromewebstore.google.com/detail/sf-api-explorer)
Not affiliated with Salesforce. Repo name: `sf-api-explorer`.
```text
sf-api-explorer/
packages/core shared discovery + OpenAPI generation
packages/ui shared explorer UI (adapter-based)
apps/web Next.js (OAuth Connected App + /api/sf proxy)
apps/extension Chrome/Edge MV3 (sid cookie, OAuth fallback)
apps/mcp local stdio MCP (Salesforce CLI auth)
```
**Change rule:** Salesforce API shape, OpenAPI output, and catalog filters change only in `packages/core`. Explorer screens change in `packages/ui`. Apps may only change auth, routing, and transport.
## Prerequisites
- Node.js 20+
- For the web app: a Salesforce Connected App
- For the MCP server: Salesforce CLI (`sf`) and an authorized org
## Web app (`apps/web`)
OAuth authorization-code + PKCE. Tokens stay in encrypted httpOnly cookies (6 hours from login). Try-it-out goes through `/api/sf`.
```bash
cp .env.example apps/web/.env.local
# fill SF_CLIENT_ID, SF_CLIENT_SECRET, SF_REDIRECT_URI, SESSION_SECRET
# do not commit .env.local
npm install
npm run dev
```
Open http://localhost:3000 and log in with Salesforce.
Register the Connected App callback URL so it matches `SF_REDIRECT_URI`:
- Local: `http://localhost:3000/api/auth/callback`
- Hosted: `https://<your-host>/api/auth/callback`
Keep both callbacks on the Connected App if you run locally and in production. `SESSION_SECRET` must be a 32+ byte secret used to encrypt session cookies.
## Browser extension (`apps/extension`)
Chrome and Edge, Manifest V3. By default the service worker reads the Salesforce `sid` cookie from orgs you are already logged into and calls REST as `Authorization: Bearer {sid}`. If the org rejects that session (API Access Control), the explorer can fall back to Connected App OAuth (PKCE, no client secret).
### Load unpacked
```bash
npm install
npm run dev:ext
```
WXT prints an output directory (typically `apps/extension/.output/chrome-mv3-dev`).
1. Open `chrome://extensions` (or Edge `edge://extensions`).
2. Enable Developer mode.
3. Load unpacked → select that output folder.
4. Log in to a Salesforce sandbox in another tab.
5. Click the extension icon, pick the org, open the explorer.
6. Generate the spec, try `GET /limits`, download YAML.
To rebuild without the watcher: `npm run build:ext`. Load `apps/extension/.output/chrome-mv3`.
### Permissions
The store / install prompt will say the extension can read Salesforce site data. That is required:
- `cookies` — the `sid` cookie is HttpOnly, so page scripts cannot see it.
- `tabs` — list open Salesforce tabs for the org picker.
- `identity` — optional Connected App login when the sid cookie cannot call the API.
- `storage` — session-scoped OAuth tokens (never `localStorage`).
- Host access to `*.salesforce.com`, `*.force.com`, `*.cloudforce.com`, `*.salesforce.mil` — call REST and Tooling APIs directly.
The session ID is equivalent to the logged-in user. Try-it POST/PATCH/DELETE mutates org data. Session IDs stay in memory (`sessionStorage` only stores the host name). OAuth tokens are stored in `chrome.storage.session`.
### API Access Control
Orgs that require a Connected App for API access reject cookie `sid` REST calls. The explorer then offers **Sign in with Salesforce**.
Prefer a **separate public Connected App** for the extension (PKCE, no client secret). Do not disable “Require Secret for Web Server Flow” on the web app’s confidential Connected App.
1. Copy `apps/extension/.env.example` to `apps/extension/.env`.
2. Create a Connected App for the extension. Uncheck **Require Secret for Web Server Flow**. Set `WXT_SF_CLIENT_ID` to that consumer key.
3. Add the extension Callback URL: `https://<extension-id>.chromiumapp.org/` (shown in the explorer error if login is not configured yet). Unpacked IDs are stable once you reload the extension.
Lightning `sid` cookies are often not API-capable. The extension maps `*.lightning.force.com` to `*.my.salesforce.com` and matches cookies by org id (`00D…!…`).
## MCP server
The MCP server maps an authorized Salesforce org to OpenAPI 3.1 (REST + Apex REST). Agents can search the surface, hydrate sObject field schemas, fetch one operation as a PathItem, and export YAML/JSON.
It does **not** run SOQL or create, update, or delete records. Use [Salesforce DX MCP](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_mcp.htm) (or the explorer try-it-out UI) for that.
Two ways to run it:
| Transport | Auth | Where |
|---|---|---|
| **Hosted HTTP** at `/mcp` | Same Connected App OAuth as the web app | `apps/web` |
| **Local stdio** | Salesforce CLI (`sf`) | `apps/mcp` |
### Tools
| Tool | Purpose |
|---|---|
| `sf-api-explorer_status` | Connected org and whether a catalog is cached |
| `sf-api-explorer_generate_catalog` | Discover REST + Apex REST and cache a compact catalog (call once per session) |
| `sf-api-explorer_search_sobjects` | Search sObjects by API name or label |
| `sf-api-explorer_search_operations` | Search operations (query, composite, sobjects, Apex REST) |
| `sf-api-explorer_describe_sobjects` | Hydrate field-level schemas for up to 50 sObjects |
| `sf-api-explorer_get_operation` | One OpenAPI PathItem + referenced schemas |
| `sf-api-explorer_export_spec` | Full cached document (YAML or JSON) |
Typical flow: `generate_catalog` once, then `search_sobjects` / `search_operations`, `describe_sobjects` if field schemas are missing, `get_operation` for one PathItem, `export_spec` to write or return the document.
### Hosted MCP (`apps/web`)
The Next.js app serves Streamable HTTP MCP at `/mcp`, using the **same Salesforce Connected App** and encrypted session as the explorer (cookie or `Authorization: Bearer`). MCP clients are sent through MCP OAuth, which wraps `/api/auth/login` → Salesforce → `/api/auth/callback`.
Add a remote / HTTP MCP server pointing at `https://<your-host>/mcp` (hosted: `https://sf-api-explorer.com/mcp`; local: `http://localhost:3000/mcp`). Examples:
```bash
claude mcp add --transport http sf-api-explorer https://sf-api-explorer.com/mcp
codex mcp add sf-api-explorer --url https://sf-api-explorer.com/mcp
```
Many clients also accept a project MCP JSON file:
```json
{
"mcpServers": {
"sf-api-explorer": {
"type": "http",
"url": "https://sf-api-explorer.com/mcp"
}
}
}
```
Codex uses TOML in `~/.codex/config.toml` instead of JSON. The first connect shows a sandbox vs production picker, then Salesforce login. After that, ask the assistant to generate the catalog and search operations.
`sf-api-explorer_export_spec` returns the document inline (no server filesystem write). Generating and describing endpoints uses the org's Salesforce API allocation.
The hosted app also has a how-to page at `/mcp-guide`. Agents can read [llms.txt](https://sf-api-explorer.com/llms.txt).
To list the hosted server on the [official MCP Registry](https://modelcontextprotocol.io/registry), publish the repo-root `server.json` after GitHub login:
```bash
mcp-publisher login github
mcp-publisher publish
```
### Local MCP (`apps/mcp`)
Stdio MCP for coding assistants (Claude Code, Codex, Cursor, and other MCP clients). Requires the [Salesforce CLI](https://developer.salesforce.com/tools/salesforcecli) on `PATH` and an authorized org (`sf org login web`). Independent of the web app: no Next.js process, no session cookie.
From the repo root, register a stdio MCP server (working directory must be the repo root so `@sf2api/core` resolves):
```json
{
"mcpServers": {
"sf-api-explorer": {
"command": "npx",
"args": [
"tsx",
"apps/mcp/src/index.ts",
"--target-org",
"DEFAULT_TARGET_ORG"
]
}
}
}
```
Replace `DEFAULT_TARGET_ORG` with a CLI alias (`sf org list`). Optional `--api-version 61.0`. Working directory must be the repo root so `@sf2api/core` resolves.
`sf-api-explorer_export_spec` writes a file under the working directory when you pass `path`.
```bash
npm run dev:mcp -- --target-org DEFAULT_TARGET_ORG
```
## Scripts
| Command | What it does |
|---|---|
| `npm run dev` | Next.js web app |
| `npm run dev:ext` | WXT Chrome extension (watch) |
| `npm run build` | Production web build |
| `npm run build:ext` | Production Chrome extension |
| `npm run zip:ext` | Zip the Chrome extension for store upload |
| `npm run dev:mcp` | Local sf-api-explorer MCP server (stdio; pass `-- --target-org ALIAS`) |
| `npm run lint` | ESLint on the web app |
| `npm test` | Unit tests for `@sf2api/core`, `@sf2api/ui`, `@sf2api/mcp`, and `@sf2api/web` |
## Version history
The web app and extension share one version. History is [CHANGELOG.md](./CHANGELOG.md). Pushing a `v*.*.*` tag creates a GitHub Release from that changelog section.
To cut a release:
1. Bump `version` in every `package.json` and in `apps/extension/wxt.config.ts`.
2. Move `[Unreleased]` notes into a new `## [x.y.z] - YYYY-MM-DD` section and update the compare links at the bottom.
3. Commit, tag `vx.y.z`, and push the tag (`git push origin vx.y.z`).
## Docker (web only)
`.dockerignore` excludes extension **source** (package.json stays so workspaces resolve). Put secrets in `apps/web/.env.local`, then:
```bash
docker compose up --build
```
The container listens on `127.0.0.1:3000`. Liveness: `GET /api/health`. For a public hostname, put a reverse proxy in front of that port and set `SF_REDIRECT_URI` (and the Connected App callback) to `https://<your-host>/api/auth/callback`.
## License
MIT. See [LICENSE](./LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues