IKI Salesforce Product MCP Server

IKI Salesforce Product MCP Server

Enables creation and updates of Salesforce Product2 records via the REST API with write-only scoped access, requiring confirmation before actual writes.

Category
访问服务器

README

IKI Salesforce Product MCP Server

A private remote Model Context Protocol (MCP) server that lets Claude create and update Salesforce Product2 records via the REST API. Deployed on Vercel. Write-only, scoped to Product2 — it does not expose SOQL or any other object. That's intentional: it's a sibling to a separate read-only server (soql_query, account_intel), kept apart so the token that can mutate CRM data is distinct from — and smaller in blast radius than — the one that only reads it.

  • Endpoint: https://salesforce-product-mcp.vercel.app/api/mcp (Streamable HTTP)
  • Tools: create_product2, update_product2, start_bulk_product2_job, get_bulk_product2_job_status
  • Transport: Streamable HTTP only (no SSE, no Redis)
  • Runtime: Node.js (not Edge)

Why this shape

Salesforce here is Professional Edition + the paid API add-on. Salesforce's hosted MCP servers require Enterprise+, and the local DX MCP server isn't the target, so this server calls the Salesforce REST API directly (v66.0). Auth to Salesforce uses an External Client App (ECA) with the Client Credentials flow — new Connected Apps are blocked as of Spring '26.

Architecture — two auth layers

  1. Claude → this server: a shared bearer token (MCP_BEARER_TOKEN). This token can write CRM data, so treat it as more sensitive than a read-only token and use a different one than any sibling read-only server. Requests without a valid token get a 401 (never a 500).
  2. This server → Salesforce: the OAuth 2.0 Client Credentials flow. The server POSTs to ${SF_LOGIN_URL}/services/oauth2/token, caches the returned access_token + instance_url at module scope, and reuses that token across warm invocations to stay within the Professional Edition API quota. A 401 mid-flight triggers a single refresh-and-retry.

Tools

Both tools share two guards:

  1. Field-name sanitization (lib/product2.ts) — every key in fields must match a plain Salesforce API-name pattern (leading letter, alphanumerics/underscores, optional __c custom-field suffix). Id, attributes, and relationship paths (e.g. Owner.Name) are rejected outright, so a write can only ever set flat scalar fields on the target record — never traverse into a related object or override the identity fields Salesforce manages itself.
  2. Confirm-before-write — without confirm: true, both tools return a dry-run preview ({ dryRun: true, action, fields, ... }) showing exactly what would be sent, without calling Salesforce. Only confirm: true performs the actual POST/PATCH.

create_product2(fields, confirm?)

Creates a Product2 record. fields must include a non-empty Name (Salesforce rejects a Product2 insert without one).

update_product2(id, fields, confirm?)

Updates an existing Product2 record by Id.

⚠️ Before using either tool, the ECA's Client Credentials "Run As" user needs Create + Edit object permissions on Product2 (and field-level security on any custom fields you intend to write) — set this via a Permission Set assigned to that user in Salesforce Setup. Object-level CRUD is normally gated by the Run As user's permissions rather than the ECA's OAuth scope list, but verify this against Salesforce Setup / current Salesforce docs for your org's edition before relying on it — this is an area that shifts across Salesforce releases.

Bulk import (Bulk API 2.0)

create_product2/update_product2 write one record per REST call — fine for ad-hoc edits, but not for a multi-thousand-row CSV import (the kind previously done via dataloader.io). start_bulk_product2_job and get_bulk_product2_job_status add that via Salesforce's Bulk API 2.0, which handles orders of magnitude more data far more efficiently than looping single-record calls (and without burning through the Professional Edition API add-on's daily quota).

The actual CSV bytes never pass through an MCP tool call. A real import file (several thousand rows) is roughly 200k+ tokens of raw text — far too much to embed in a tool-call argument: slow, expensive, and it risks transcription errors as an LLM copies that much data through its own context. Instead:

  1. Call start_bulk_product2_job with operation (insert/update/ upsert), the total rowCount, and a small sample (1-10 rows) of the real data for validation. Without confirm: true, this only validates the sample and returns a preview — no Salesforce call is made.
  2. With confirm: true, it opens a real Bulk API job and returns a relative uploadPath (e.g. /api/bulk/{jobId}/data).
  3. From a shell-capable session (Claude Code or similar — not the Desktop/claude.ai chat connector, which has no way to stream a local file outside the tool-call schema), stream the entire CSV file to that path in one request:
    curl --data-binary @file.csv \
      -H "Authorization: Bearer <MCP_BEARER_TOKEN>" \
      -X PUT "https://salesforce-product-mcp.vercel.app/api/bulk/<jobId>/data"
    
    This closes the job and starts Salesforce processing it. The row data never enters the calling model's context — curl reads it straight off disk.
  4. Poll get_bulk_product2_job_status (an MCP tool call — cheap, small response) until state is JobComplete. If any rows failed, the result includes a capped summary (default: first 25 failures + a total count) — never a full multi-thousand-row result dump.

No chunking — one PUT per job, verified against a real org. An earlier version of this doc (and the route) assumed Salesforce's Bulk API 2.0 would accept multiple sequential PUT calls to build up a job's data before closing it. Live testing proved that wrong: a second PUT .../batches call on the same job is rejected outright ("Found multiple contents for job"). The entire CSV must go in a single request. The real, unavoidable ceiling is Vercel's Node.js serverless request-body limit (~4.5 MB, not tunable from Next.js config — that only applies to the legacy Pages API), so a file needs to fit under roughly 4.4 MB to be safe. For context: a typical Product2 row (a handful of fields) is well under 1 KB, so even a 6,500-row file is normally a few hundred KB — comfortably under that ceiling. A file that genuinely exceeds it would need to be split across multiple separate Bulk API jobs (each with its own single PUT) — not built here, since it wasn't needed for what this server was built for.

Row cap: start_bulk_product2_job rejects a declared rowCount above MAX_ROW_COUNT (50,000, in lib/bulkProduct2.ts). This is a self-imposed mistake-guard against pointing at the wrong file — not a Salesforce limit; Bulk API 2.0 itself handles far more than that.

Operation-specific row rules (validated against the sample you pass to start_bulk_product2_job, and enforced by Salesforce on the real file):

  • insert — rows must NOT include an Id column.
  • update — every row MUST include an Id column (the record to update).
  • upsert — every row MUST include the externalIdFieldName column, and must NOT include Id.

⚠️ Upsert requires Salesforce-side setup that isn't done automatically: the field you pass as externalIdFieldName (e.g. ProductCode) must already have the External ID checkbox enabled in Salesforce Object Manager → Product2 → Fields & Relationships. It is not on by default for standard fields like ProductCode — a job created with an unqualified field will fail Salesforce-side.

Environment variables

See .env.example. Copy it to .env.local for local dev; never commit real secrets.

Variable Purpose
SF_LOGIN_URL Org My Domain URL (not login.salesforce.com)
SF_CLIENT_ID ECA Consumer Key
SF_CLIENT_SECRET ECA Consumer Secret
MCP_BEARER_TOKEN Shared bearer token for Claude → server (openssl rand -hex 32)

Local development

npm install
cp .env.example .env.local   # fill in values
npm run dev                  # http://localhost:3000, endpoint /api/mcp

Verify the build (no live credentials needed)

npm run typecheck   # tsc --noEmit
npm run build       # next build
npm test            # vitest — mocks the SF token + REST responses

Prove the auth gate

With the server running (npm run dev or npm run start):

MCP_BEARER_TOKEN=<your-token> npm run check-auth
# or against the live deployment:
MCP_BEARER_TOKEN=<your-token> node scripts/check-auth.mjs https://salesforce-product-mcp.vercel.app

Expected: the request without a token returns 401; the request with the correct token is accepted.

Inspect with the MCP Inspector

npx @modelcontextprotocol/inspector

Point it at http://localhost:3000/api/mcp (Streamable HTTP) with an Authorization: Bearer <MCP_BEARER_TOKEN> header. You should see create_product2 and update_product2 listed. Try a call without confirm: true first to see the dry-run preview before writing anything.

Deployment (Vercel)

This is IKI business use, so it must be deployed to a Vercel Pro team, not a personal/Hobby account (Hobby prohibits commercial use).

vercel link                          # link to the Pro team/project
vercel env add SF_LOGIN_URL          # repeat for each variable, all environments
vercel env add SF_CLIENT_ID
vercel env add SF_CLIENT_SECRET
vercel env add MCP_BEARER_TOKEN
vercel                               # preview deploy — verify /api/mcp
vercel --prod                        # promote to production
  • Fluid compute keeps functions warm, which helps the module-scoped Salesforce token cache survive between requests. Enable it in Project → Settings → Functions.
  • The URL registered as the Claude connector must be the stable production domain (https://<project>.vercel.app/api/mcp or a custom domain). Preview URLs change on every deploy and would silently break the connector.

Connecting from Claude

Settings → Connectors → Add custom connector. Name it something that signals its blast radius — e.g. "Salesforce Product Write (USE CAUTION!)" — since it's easy to lose track of which connector can write CRM data once you have several. Use a bearer token distinct from the read-only sibling server's, so the two can be rotated/revoked independently.

The dialog's auth options vary by rollout:

  • If there's a "Request headers" field (beta, gradually rolled out): set header Authorization to Bearer <MCP_BEARER_TOKEN> (space after Bearer), and use the plain endpoint URL.
  • If the dialog only shows OAuth Client ID/Secret (no headers field — the common case as of this writing): leave those blank and instead bake the token into the URL itself, using the server's built-in ?token= fallback:
    https://salesforce-product-mcp.vercel.app/api/mcp?token=<MCP_BEARER_TOKEN>
    
    This isn't a workaround bolted onto the docs — extractToken() in route.ts accepts the token via either the Authorization header or a ?token=/?key= query param specifically because claude.ai's connector dialog has historically had no bearer-token field for servers without an OAuth authorization server.

Under Connection methods, leave Individual sign-in on and skip Managed authorization. Managed authorization connects your whole workspace through your identity provider with no per-member opt-in — the opposite of what you want for a connector that can write to Product2. It also targets OAuth/SSO connectors; this server doesn't do OAuth, so it isn't a fit here regardless.

Once connected, run a dry-run call first (omit confirm) to confirm the round trip works before doing a real write.

This connector setup covers create_product2/update_product2 fine, but bulk imports (see above) need a shell-capable session, not this chat connector — the curl-based upload step has no equivalent in a pure chat interface.

Operational notes

Professional Edition's API add-on has a modest daily API call quota. This server makes only 1–2 REST calls per write and reuses its cached token rather than re-authenticating each request. Watch usage at:

Salesforce Setup → Company Information → API Requests, Last 24 Hours

Notes on dependencies

  • @modelcontextprotocol/sdk is pinned to 1.26.0 (mcp-handler's peer; ≥ 1.26.0 is required — earlier versions have a security vulnerability).

Project layout

app/
  layout.tsx
  page.tsx
  api/
    [transport]/route.ts    # MCP handler: create_product2, update_product2,
                             #   start_bulk_product2_job, get_bulk_product2_job_status
    bulk/[jobId]/data/route.ts  # plain HTTP PUT endpoint for streaming CSV bytes
                                 #   (not an MCP tool) -- one PUT per job, no chunking
lib/
  auth.ts                    # shared bearer-token check, used by both routes above
  salesforce.ts              # Client Credentials auth, token cache, REST helper (sfFetch)
  product2.ts                # Field-name sanitization + single-record create/update
  bulkProduct2.ts             # Bulk API 2.0 client + row/field validation
scripts/
  check-auth.mjs             # proves the bearer gate (with/without token)
tests/
  auth.test.ts               # bearer-token extraction/comparison unit tests
  salesforce.test.ts         # mocked token + REST call smoke test
  product2.test.ts           # field sanitization + mocked create/update smoke test
  bulkProduct2.test.ts        # bulk validation rules + mocked Bulk API client calls

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选