MCP server by nh916
MCP Salesforce Integration
A lightweight MCP (Model Context Protocol) server that exposes Salesforce Contacts and Appointments (Events) as callable tools for LLM agents.
This project implements:
-
OAuth2 refresh-token authentication
-
Automatic access token refresh
-
Typed request models with Pydantic
-
Clean Salesforce REST client abstraction
-
MCP tool registration layer
-
Full CRUD for:
- Contacts (
Contact) - Appointments (
Event)
- Contacts (
Architecture
flowchart TD
A[Claude / LLM] --> B[MCP Server - FastMCP]
B --> C[SalesforceClient]
C --> D[Salesforce REST API]
Key Components
config.py— Environment configuration via Pydanticsalesforce/client.py— Auth + REST logicsalesforce/models.py— Pydantic request modelstools/— MCP tool wrappersserver.py— MCP server entrypoint
Features
Contacts
- Create
- Get
- Update
- Delete
- List
Appointments (Salesforce Event)
- Create
- Get
- Update
- Delete
- List
Setup
1. Install dependencies
poetry install
2. Configure environment variables
Create a .env file in project root:
SALESFORCE_CLIENT_ID=...
SALESFORCE_CLIENT_SECRET=...
SALESFORCE_REFRESH_TOKEN=...
SALESFORCE_INSTANCE_URL=https://your-instance.salesforce.com
SALESFORCE_API_VERSION=v60.0
Running the MCP Server
poetry run python -m mcp_salesforce.server
Connecting to Claude Code
Register your server:
claude mcp add salesforce "poetry run python -m mcp_salesforce.server"
Verify:
claude mcp list
Start Claude:
claude
Example prompt:
Use salesforce_list_contacts with limit 3
Authentication Strategy
-
Stores long-lived
refresh_tokenin.env -
Access token generated via
/services/oauth2/token -
On
INVALID_SESSION_ID:- Refresh token
- Retry request once
-
Stateless beyond in-memory access token
No manual re-authentication required once refresh token is configured.
Design Decisions
- Single shared
SalesforceClient - Central
_make_requestmethod to avoid duplication - Retry-once logic for expired sessions
- Pydantic validation at tool boundary
- Strict Python 3.11 typing (
dict,| None, etc.)
Example Tool Usage
Create Contact:
{
"FirstName": "John",
"LastName": "Doe",
"Phone": "555-123-4567",
"Email": "john@example.com"
}
Create Appointment:
{
"Subject": "Appointment",
"StartDateTime": "2026-02-20T18:00:00.000+0000",
"EndDateTime": "2026-02-20T18:30:00.000+0000",
"WhoId": "003XXXXXXXXXXXX"
}
Future Improvements
- Return typed response models instead of raw dicts
- Structured error model for Salesforce API errors
- Logging + observability
- Pagination handling
- Async version of client